# JavaScript SDK

## Download and Installation

### **Install with NPM**

```bash
npm install @keeper-security/secrets-manager-core
```

For more information, see <https://www.npmjs.com/package/@keeper-security/secrets-manager-core>

### **Source Code**

Find the JavaScript source code in the [GitHub repository](https://github.com/Keeper-Security/secrets-manager/tree/master/sdk/javascript)

## Using the SDK

### Initialize Storage

{% hint style="info" %}
Using token only to generate a new config (for later usage) requires at least one read operation to bind the token and fully populate `config.json`
{% endhint %}

In order to retrieve secrets, you must first initialize the local storage on your machine.

```javascript
initializeStorage(storage: KeyValueStorage, clientKey: String? = null, hostName: String? = null)
```

| Parameter   | Type              | Required | Default | Description                                                                            |
| ----------- | ----------------- | -------- | ------- | -------------------------------------------------------------------------------------- |
| `storage`   | `KeyValueStorage` | Yes      |         | storage location                                                                       |
| `clientKey` | string            | Optional | null    | token for connecting with Keeper Secrets Manager                                       |
| `hostName`  | string            | Optional | null    | server location to get secrets. If nothing is passed, will use keepersecurity.com (US) |

#### **Example Usage**

```javascript
const { getSecrets, initializeStorage, localConfigStorage } = require('@keeper-security/secrets-manager-core')

const getKeeperRecords = async () => {

    // oneTimeToken is used only once to initialize the storage
    // after the first run, subsequent calls will use ksm-config.txt
    const oneTimeToken = "<One Time Access Token>";
    
    const storage = localConfigStorage("ksm-config.json")
    
    await initializeStorage(storage, oneTimeToken)
    // Using token only to generate a config (for later usage)
    // requires at least one access operation to bind the token
    //await getSecrets({storage: storage})
    
    const {records} = await getSecrets({storage: storage})
    console.log(records)

    const firstRecord = records[0]
    const firstRecordPassword = firstRecord.data.fields.find(x => x.type === 'password')
    console.log(firstRecordPassword.value[0])
}

getKeeperRecords().finally()
```

### Retrieve Secrets

```javascript
getSecrets(options: SecretsManagerOptions, recordsFilter: List<String> = emptyList()): KeeperSecrets
```

| Parameter       | Type              | Required | Default    | Description        |
| --------------- | ----------------- | -------- | ---------- | ------------------ |
| `storage`       | `KeyValueStorage` | Yes      |            | Storage location   |
| `recordsFilter` | string\[]         | Optional | Empty List | Record UIDs to get |

**Response**

Type: `KeeperSecrets`

Object containing all Keeper records, or records that match the given filter criteria

**Example Usage**

Retrieve all Secrets

```javascript
const storage = inMemoryStorage() // see initialization example
const secrets = await getSecrets({storage: storage})
```

### Retrieve Secrets by Title

```javascript
// get all matching records
getSecretsByTitle = async (options: SecretManagerOptions, recordTitle: string): Promise<KeeperRecord[]>

// get only the first matching record
getSecretByTitle = async (options: SecretManagerOptions, recordTitle: string): Promise<KeeperRecord>
```

<table><thead><tr><th width="178.10385756676556">Parameter</th><th width="150">Type</th><th width="150">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>options</code></td><td>SecretsManagerOptions</td><td>Yes</td><td>Preconfigured options</td></tr><tr><td><code>recordTitle</code></td><td>string</td><td>Yes</td><td>Record title to search for</td></tr></tbody></table>

**Example Usage**

```javascript
const {
    getSecretByTitle,
    localConfigStorage,
} = require('@keeper-security/secrets-manager-core')

const getKeeperRecord = async () => {
    const options = { storage: localConfigStorage("ksm-config.json") }
    const myCredential = await getSecretByTitle(options, "My Credential")
}

getKeeperRecord().finally()
```

### Retrieve Values From a Secret

**Retrieve a Password**

{% tabs %}
{% tab title="Get Password" %}

```javascript
secret.data.fields.find(x => x.type === 'password')
```

{% endtab %}

{% tab title="Example Usage" %}

```javascript
const { getSecrets, initializeStorage, localConfigStorage } = require('@keeper-security/secrets-manager-core')

const getKeeperRecords = async () => {
    const storage = localConfigStorage("ksm-config.json")
    // get records
    const {records} = await getSecrets({storage: storage})
    // get password from first record
    const firstRecord = records[0]
    const firstRecordPassword = firstRecord.data.fields.find(x => x.type === 'password')
}
```

{% endtab %}
{% endtabs %}

Field types are based on the Keeper [Record Type](https://docs.keeper.io/enterprise-guide/record-types). For a detailed list of available fields based on the Keeper Record Type, see the [record-type-info](https://docs.keeper.io/en/keeperpam/commander-cli/command-reference/record-commands/record-type-commands#record-type-info-command) command in Keeper Commander.

**Retrieve other Fields with Keeper Notation**

{% tabs %}
{% tab title="Get Value" %}

```javascript
getValue(secrets: KeeperSecrets, query: string): any
```

{% endtab %}

{% tab title="Example Usage" %}

```javascript
const {
    getSecrets,
    localConfigStorage,
    getValue
} = require('@keeper-security/secrets-manager-core')

const getKeeperRecords = async () => {
    const options = { storage: localConfigStorage("ksm-config.json") }
    
    // get secrets
    const secrets = await getSecrets(options)

    // get login with dot notation
    const loginValue = getValue(secrets, 'RECORD_UID/field/login')
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
See [Keeper Notation documentation](https://docs.keeper.io/en/keeperpam/secrets-manager/about/keeper-notation) to learn about Keeper Notation format and capabilities
{% endhint %}

| Parameter | Type            | Required | Default | Description           |
| --------- | --------------- | -------- | ------- | --------------------- |
| `secrets` | `KeeperSecrets` | Yes      |         | Secrets to query      |
| `query`   | `string`        | Yes      |         | Keeper Notation query |

#### **Returns**

Type: `any`

The value of the field at the location specified by the dot notation query if any, otherwise undefined.

### **Retrieve a TOTP Code**

{% tabs %}
{% tab title="Get TOTP Code" %}

```javascript
getTotpCode(
    url: string,
    unixTimeSeconds?: number
): Promise<{
    code: string
    timeLeft: number
    period: number
} | null>
```

{% endtab %}

{% tab title="Example" %}

```javascript
const {
    getSecrets,
    localConfigStorage,
    getTotpCode,
    getValue} = require('@keeper-security/secrets-manager-core')

const getKeeperRecords = async () => {
    const options = { storage: localConfigStorage("ksm-config.json") }

    // get secrets
    const secrets = await getSecrets(options)

    // get TOTP URI from record using notation
    const totpUri = getValue(secrets, 'RECORD_UID/field/oneTimeCode')

    // get TOTP code
    const totpResult = await getTotpCode(totpUri)

    if (totpResult) {
        console.log(`TOTP Code: ${totpResult.code}`)
        console.log(`Expires in: ${totpResult.timeLeft} seconds`)
        console.log(`Period: ${totpResult.period} seconds`)
    } else {
        console.log('Invalid TOTP URL')
    }
}

getKeeperRecords().finally()
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
See [Keeper Notation documentation](https://docs.keeper.io/en/keeperpam/secrets-manager/about/keeper-notation) to learn about Keeper Notation format and capabilities
{% endhint %}

| Parameter         | Type     | Required | Default           | Description                                          |
| ----------------- | -------- | -------- | ----------------- | ---------------------------------------------------- |
| `url`             | `string` | Yes      |                   | TOTP Url                                             |
| `unixTimeSeconds` | `number` | Optional | Current timestamp | Unix timestamp in seconds (for testing/verification) |

\* The record UID in the notation query must be for a secret passed in the secrets parameter or nothing will be found by the query

#### **Returns**

Type: `Promise<{code: string, timeLeft: number, period: number} | null>`

An object containing:

* `code` - The TOTP code (6-8 digits)
* `timeLeft` - Seconds remaining until code expires
* `period` - TOTP period in seconds (typically 30)

Returns `null` if the TOTP URL is invalid or cannot be processed.

### Update a Secret

{% hint style="warning" %}
Record update commands don't update local record data on success *(esp. updated record revision)* so any consecutive updates to an already updated record will fail due to **revision** mismatch. Make sure to reload all updated records after each update batch.
{% endhint %}

{% tabs %}
{% tab title="Update Secret" %}

```javascript
updateSecret(options, record)
```

{% endtab %}

{% tab title="Example Usage" %}

<pre class="language-javascript"><code class="lang-javascript"><strong>const { 
</strong>    getSecrets, 
    localConfigStorage, 
    updateSecret} = require('@keeper-security/secrets-manager-core')

const storage = localConfigStorage("ksm-config.json")

// get records
const {records} = await getSecrets({storage: storage})

// get the first record
const recordToUpdate = records[0]

// set new record title and notes
recordToUpdate.data.title = 'New Title'
recordToUpdate.data.notes = "New Notes"

// save record changes
await updateSecret(options, recordToUpdate)
</code></pre>

{% endtab %}

{% tab title="Transactional updates" %}

<pre class="language-javascript"><code class="lang-javascript"><strong>const { 
</strong>    getSecrets, 
    localConfigStorage, 
    updateSecret,
    completeTransaction,
    UpdateTransactionType,
    SecretManagerOptions
} = require('@keeper-security/secrets-manager-core')

// get records
const options = { storage: localConfigStorage("ksm-config.json") }
const {records} = await getSecrets(options)

// rotate password on the first record
const secret = records[0]
const password = secret.data.fields.find(x => x.type === "password")
password.value[0] = "MyNewPassword"

//start a transaction to update record in vault
await updateSecret(options, secret, UpdateTransactionType.Rotation)

// rotate password on remote host
const success = rotateRemoteSshPassword("MyNewPassword");

// complete the transaction - commit or rollback
const rollback = !success
await completeTransaction(options, secret.recordUid, rollback)
</code></pre>

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="150">Parameter</th><th width="257">Type</th><th width="150">Required</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td>Parameter</td><td>Type</td><td>Required</td><td>Default</td><td>Description</td></tr><tr><td><code>options</code></td><td><code>SecretManagerOptions</code></td><td>Yes</td><td></td><td></td></tr><tr><td><code>record</code></td><td><code>KeeperRecord</code></td><td>Yes</td><td></td><td></td></tr></tbody></table>

**Returns**

**Type:** `Promise<void>`

### Generate a Random Password

{% tabs %}
{% tab title="Generate Password" %}

```javascript
generatePassword(length, lowercase, uppercase, digits, specialCharacters)
```

{% endtab %}

{% tab title="Example Usage" %}

```javascript
const { 
    getSecrets, 
    localConfigStorage, 
    updateSecret, 
    generatePassword } = require('@keeper-security/secrets-manager-core')

// generate a random password
let newRandomPwd = await generatePassword()

const storage = localConfigStorage("ksm-config.json")
const {records} = await getSecrets({storage: storage})

// get the first record
const recordToUpdate = records[0]

// Find the field with the type "password"
const recordToUpdatePasswordField = recordToUpdate.data.fields.find(x => x.type === 'password')

// set new value to the password field
recordToUpdatePasswordField.value[0] = newRandomPwd

await updateSecret({storage: storage}, recordToUpdate)
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="203.7142857142857">Parameter</th><th width="150">Type</th><th width="150">Required</th><th>Default</th></tr></thead><tbody><tr><td>length</td><td><code>int</code></td><td>Optional</td><td>64</td></tr><tr><td>lowercase</td><td><code>int</code></td><td>Optional</td><td>0</td></tr><tr><td>uppercase</td><td><code>int</code></td><td>Optional</td><td>0</td></tr><tr><td>digits</td><td><code>int</code></td><td>Optional</td><td>0</td></tr><tr><td>specialCharacters</td><td><code>int</code></td><td>Optional</td><td>0</td></tr></tbody></table>

Each parameter indicates the min number of a type of character to include. For example, 'uppercase' indicates the minimum number of uppercase letters to include.

#### Returns

Type: `String`

### Download a File

```javascript
downloadFile(file: KeeperFile): ByteArray
```

| Parameter | Type         | Required | Default | Description      |
| --------- | ------------ | -------- | ------- | ---------------- |
| `file`    | `KeeperFile` | Yes      |         | File to download |

**Response**

Type: `Promise<Uint8Array`

Bytes of file for download

### Download a **Thumbnail**

```javascript
downloadThumbnail(file: KeeperFile): ByteArray
```

| Parameter | Type         | Required | Default | Description                     |
| --------- | ------------ | -------- | ------- | ------------------------------- |
| `file`    | `KeeperFile` | Yes      |         | File with thumbnail to download |

**Response**

Type: `Promise<Uint8Array>`

Bytes of thumbnail for download

### Upload a File

Upload File:

```javascript
uploadFile = async (options: SecretManagerOptions, ownerRecord: KeeperRecord, file: KeeperFileUpload): Promise<string>
```

<table><thead><tr><th width="184.14763231197773">Parameter</th><th width="246.99431983802478">Type</th><th width="150">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>options</code></td><td>SecretsManagerOptions</td><td>Yes</td><td>Storage and query configuration</td></tr><tr><td><code>ownerRecord</code></td><td>KeeperRecord</td><td>Yes</td><td>The record to attach the uploaded file to</td></tr><tr><td><code>file</code></td><td>KeeperFileUpload</td><td>Yes</td><td>The File to upload</td></tr></tbody></table>

Creating the Keeper File Upload Object:

```javascript
type KeeperFileUpload = {
    name: string
    title: string
    type?: string
    data: Uint8Array
}
```

<table><thead><tr><th width="169">Parameter</th><th width="150">Type</th><th width="150">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td>string</td><td>Yes</td><td>What the name of the file will be in Keeper once uploaded</td></tr><tr><td><code>title</code></td><td>string</td><td>Yes</td><td>What the title of the file will be in Keeper once uploaded</td></tr><tr><td><code>type</code></td><td>string</td><td>Optional</td><td>The mime type of data in the file. 'application/octet-stream' will be used if nothing is given</td></tr><tr><td><code>data</code></td><td>Uint8Array</td><td>Yes</td><td>File data as bytes</td></tr></tbody></table>

**Example Usage**

```javascript
// get record to attach file to
const {records} = await getSecrets({storage: storage}, ['XXX'])
const ownerRecord = records[0]

// get file data to upload
const fileData = fs.readFileSync('./assets/my-file.json')

// upload file to selected record
await uploadFile(options, ownerRecord, {
    name: 'my-file.json',
    title: 'Sample File',
    type: 'application/json',
    data: fileData
})
```

### Create a Secret

#### Prerequisites:

* Shared folder UID
  * Shared folder must be accessible by the Secrets Manager Application
  * You and the Secrets Manager application must have edit permission
  * There must be at least one record in the shared folder
* Created records and record fields must be formatted correctly
  * See the [documentation](https://docs.keeper.io/enterprise-guide/record-types) for expected field formats for each record type
* TOTP fields accept only URL generated outside of the KSM SDK
* After record creation, you can upload file attachments using [uploadFile](#upload-a-file)

{% tabs %}
{% tab title="Create a Record" %}

<pre class="language-javascript"><code class="lang-javascript"><strong>createSecret(options, folderUid, record)
</strong></code></pre>

<table><thead><tr><th width="166">Parameter</th><th width="226">Type</th><th>Required</th><th>Default</th></tr></thead><tbody><tr><td>options</td><td>SecretManagerOptions</td><td>Yes</td><td></td></tr><tr><td>folderUid</td><td>string</td><td>Yes</td><td></td></tr><tr><td>record</td><td>JSON Object</td><td>Yes</td><td></td></tr></tbody></table>
{% endtab %}

{% tab title="Create Record in Sub-folder" %}

<pre class="language-javascript"><code class="lang-javascript"><strong>createSecret2(options, createOptions, record)
</strong></code></pre>

<table><thead><tr><th width="166">Parameter</th><th width="226">Type</th><th>Required</th><th>Default</th></tr></thead><tbody><tr><td>options</td><td>SecretManagerOptions</td><td>Yes</td><td></td></tr><tr><td>createOptions</td><td>CreateOptions</td><td>Yes</td><td></td></tr><tr><td>record</td><td>JSON Object</td><td>Yes</td><td></td></tr></tbody></table>
{% endtab %}

{% tab title="Login Record Example" %}
This example creates a login type record with a login value and a generated password.

{% hint style="info" %}
Replace '`[FOLDER UID]`' in the example with the UID of a shared folder that your Secrets Manager has access to.
{% endhint %}

```javascript
let newRec = {
    "title": "Sample KSM Record: JavaScript",
    "type": "login",
    "fields": [
        { "type": "login",    "value": [ "username@email.com" ] },
        { "type": "password", "value": [ await generatePassword() ] }
    ],
    "notes": "This is a JavaScript record creation example"
}

let recordUid = await createSecret(options, folderUid, newRec)
```

{% endtab %}

{% tab title="Custom Type Example" %}
This example creates a record with a custom record type.

{% hint style="info" %}
Replace '`[FOLDER UID]`' in the example with the UID of a shared folder that your Secrets Manager has access to.
{% endhint %}

```javascript
let newRec = {
    "title": "Sample Custom Type KSM Record: JavaScript",
    "type": "Custom Login",
    "fields": [
        {
            "type": "host",
            "label": "My Custom Host lbl",
            "value": [ {"hostName": "127.0.0.1", "port": "8080"} ],
            "required": true,
            "privacyScreen": false
        },
        {
            "type": "login",
            "label": "My Custom Login lbl",
            "value": [ "login@email.com" ],
            "required": true,
            "privacyScreen": false
        },
        {
            "type": "password",
            "label": "My Custom Password lbl",
            "value": [ await generatePassword() ],
            "required": true,
            "privacyScreen": false
        },
        {
            "type": "url",
            "label": "My Login Page",
            "value": [ "http://localhost:8080/login" ],
            "required": true,
            "privacyScreen": false
        },
        {
            "type": "securityQuestion",
            "label": "My Question 1",
            "value": [ {
                "question": "What is one plus one (write just a number)",
                "answer": "2"
            } ],
            "required": true,
            "privacyScreen": false
        },
        {
            "type": "phone",
            "value": [{
                "region": "US",
                "number": "510-444-3333",
                "ext": "2345",
                "type": "Mobile"
            }],
            "label": "My Phone Number"
        },
        {
            "type": "date",
            "value": [ 1641934793000 ],
            "label": "My Date Lbl",
            "required": true,
            "privacyScreen": false
        },
        {
            "type": "name",
            "value": [{
                "first": "John",
                "middle": "Patrick",
                "last": "Smith"
            }],
            "label": "My Custom Name lbl",
            "required": true,
            "privacyScreen": false
        },
        {
            "type": "oneTimeCode",
            "label": "My TOTP",
            "value": ["otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example"],
            "required": true,
            "privacyScreen": false
        }
    ],
    "custom": [
        {
            "type": "phone",
            "value": [{
                "region": "US",
                "number": "(510) 123-3456"
            }],
            "label": "My Custom Phone Lbl 1"
        },
        {
            "type": "phone",
            "value": [ {
                "region": "US",
                "number": "510-111-3333",
                "ext": "45674",
                "type": "Mobile" } ],
            "label": "My Custom Phone Lbl 2"
        }
    ],
    "notes": "\tThis custom type record was created\n\tvia KSM Katacoda JavaScript Example"
}

let recordUid = await createSecret(options, "[FOLDER UID]", newRec)
```

{% endtab %}
{% endtabs %}

### Delete a Secret

The JavaScript KSM SDK can delete records in the Keeper Vault.

{% tabs %}
{% tab title="Delete Secret" %}

```javascript
deleteSecret(smOptions, recordUids);
```

<table><thead><tr><th>Parameter</th><th width="250.66666666666666">Type</th><th align="center">Required</th></tr></thead><tbody><tr><td><code>smOptions</code></td><td><code>SecretManagerOptions</code></td><td align="center">Yes</td></tr><tr><td><code>recordUids</code></td><td><code>string[]</code></td><td align="center">Yes</td></tr></tbody></table>
{% endtab %}

{% tab title="Example" %}

<pre class="language-javascript"><code class="lang-javascript">// setup secrets manager
const smOptions = { storage: localConfigStorage("ksm-config.json") 

// delete a specific secret by record UID
<strong>await deleteSecret(smOptions, ["EG6KdJaaLG7esRZbMnfbFA"]);
</strong></code></pre>

{% endtab %}
{% endtabs %}

### Caching

To protect against losing access to your secrets when network access is lost, the JavaScript SDK allows caching of secrets to the local machine in an encrypted file.

Add `queryFunction: cachingPostFunction` to `SecretManagerOptions`

Example usage:

```javascript
const options: SecretManagerOptions = {    
    storage: kvs,
    queryFunction: cachingPostFunction // Import `cachingPostFunction`
}
```

### Folders

Folders have full CRUD support - create, read, update and delete operations.

### Read Folders

Downloads full folder hierarchy.

```javascript
getFolders = async (options: SecretManagerOptions): Promise<KeeperFolder[]>
```

**Response**

Type: `KeeperFolder[]`

**Example Usage**

```javascript
const storage = localConfigStorage("ksm-config.json")
const folders = await getFolders({storage: storage})
```

### Create a Folder

Requires `CreateOptions` and folder name to be provided. The folder UID parameter in `CreateOptions` is required - UID of a shared folder, while sub-folder UID is optional and if missing new regular folder is created directly under the parent (shared folder). There's no requirement for the sub-folder to be a direct descendant of the parent shared folder - it could be many levels deep.

```javascript
createFolder = async (options: SecretManagerOptions, createOptions: CreateOptions, folderName: string): Promise<string>
```

<table><thead><tr><th width="181.85161529417937">Parameter</th><th width="242.99431983802478">Type</th><th width="102">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>options</code></td><td><code>SecretsManagerOptions</code></td><td>Yes</td><td>Preconfigured options</td></tr><tr><td><code>createOptions</code></td><td><code>CreateOptions</code></td><td>Yes</td><td>The parent and sub-folder UIDs</td></tr><tr><td><code>folderName</code></td><td><code>string</code></td><td>Yes</td><td>The Folder name</td></tr></tbody></table>

```javascript
type CreateOptions = {
    folderUid: string
    subFolderUid?: string
}
```

**Example Usage**

```javascript
const storage = localConfigStorage("ksm-config.json")
const folderUid = await createFolder({storage: storage}, {folderUid: "[PARENT_SHARED_FOLDER_UID]"}, "new_folder")
```

### Update a Folder

Updates the folder metadata - currently folder name only.

```javascript
updateFolder = async (options: SecretManagerOptions, folderUid: string, folderName: string): Promise<void>
```

<table><thead><tr><th width="154.85161529417937">Parameter</th><th width="243.99431983802478">Type</th><th width="100">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>options</code></td><td><code>SecretsManagerOptions</code></td><td>Yes</td><td>Preconfigured options</td></tr><tr><td><code>folderUid</code></td><td><code>string</code></td><td>Yes</td><td>The folder UID</td></tr><tr><td><code>folderName</code></td><td><code>string</code></td><td>Yes</td><td>The new folder name</td></tr></tbody></table>

**Example Usage**

```javascript
const storage = localConfigStorage("ksm-config.json")
await updateFolder({storage: storage}, "[FOLDER_UID]", "new_folder_name")
```

### Delete Folders

Removes a list of folders. Use `forceDeletion` flag to remove non-empty folders.

{% hint style="info" %}
When using forceDeletion avoid sending parent with its children folder UIDs. Depending on the delete order you may get an error - *ex.* if parent force-deleted child first. There's no guarantee that list will always be processed in FIFO order.
{% endhint %}

{% hint style="info" %}
Any folders UIDs missing from the vault or not shared to the KSM Application will not result in error.
{% endhint %}

```javascript
deleteFolder = async (options: SecretManagerOptions, folderUids: string[], forceDeletion?: boolean): Promise<SecretsManagerDeleteResponse>
```

<table><thead><tr><th width="180.85161529417937">Parameter</th><th width="242.99431983802478">Type</th><th width="100">Required</th><th width="89">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>options</code></td><td><code>SecretsManagerOptions</code></td><td>Yes</td><td></td><td>Preconfigured options</td></tr><tr><td><code>folderUids</code></td><td><code>string[]</code></td><td>Yes</td><td></td><td>The folder UID list</td></tr><tr><td><code>forceDeletion</code></td><td><code>bool</code></td><td>No</td><td><code>false</code></td><td>Force deletion of non-empty folders</td></tr></tbody></table>

**Example Usage**

```javascript
const storage = localConfigStorage("ksm-config.json")
await deleteFolder({storage: storage}, ["[FOLDER_UID1]", "[FOLDER_UID2]"], true)
```

### Proxy Support

In order to use proxy, you need to install and set a proxy agent using `setCustomProxyAgent` from `@keeper-security/secrets-manager-core`

**Example Usage**

```javascript
const {
    getSecrets,
    initializeStorage,
    localConfigStorage,
    setCustomProxyAgent
} = require('@keeper-security/secrets-manager-core')
const { HttpsProxyAgent } = require('https-proxy-agent')

const getKeeperRecords = async () => {
    // Set you proxy URL
    setCustomProxyAgent(new HttpsProxyAgent('http://user:password@127.0.0.1:3128'))

    const storage = localConfigStorage("config.json")
    
    await initializeStorage(storage, 'US:EXAMPLE_ONE_TIME_TOKEN', 'keepersecurity.com')
    const {records} = await getSecrets({storage: storage})

    console.log(records)
}
```

{% hint style="danger" %}
`setCustomProxyAgent` works only in NodeJs environment and is not supported in browser.
{% endhint %}
