# Rust SDK

<figure><img src="https://762006384-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJXOXEifAmpyvNVL1to%2Fuploads%2F6YSOQI8zWNP3zQIkfHdI%2FKSM%20%2B%20Rust%20Icon.png?alt=media&#x26;token=18a7851d-7a71-47c1-af38-4b1970cb3250" alt=""><figcaption></figcaption></figure>

### Download and Installation

#### Requirements

{% hint style="info" %}
**Rust 1.87 or later** is required to use this SDK. This minimum version ensures compatibility with Edition 2024 dependencies and recent security patches.
{% endhint %}

#### Adding as Package using Cargo

```
cargo add keeper-secrets-manager-core
```

For more information, see <https://crates.io/crates/keeper-secrets-manager-core>

#### Source Code

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

### Using the SDK

### Initialise

{% 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 %}

{% tabs %}
{% tab title="Secrets Manager" %}

```rust
SecretsManager::new(client_options)?
```

{% endtab %}

{% tab title="Example Usage" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::FileKeyValueStorage,
    custom_error::KSMRError
};

fn main()-> Result<(), KSMRError>{
    let token = "<Your One time token>".to_string();
    let config = FileKeyValueStorage::new_config_storage("test.json".to_string())?;
    let client_options = ClientOptions::new_client_options_with_token(token, config);
    let mut secrets_manager = SecretsManager::new(client_options)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="150">Parameter</th><th width="150">Required</th><th>Description</th><th>Type</th></tr></thead><tbody><tr><td>Parameter</td><td>Required</td><td>Description</td><td>Type</td></tr><tr><td>token</td><td>Yes</td><td>One Time Access Token</td><td>String</td></tr><tr><td>config</td><td>Yes</td><td>Storage Configuration</td><td><code>KeyValueStorage</code></td></tr></tbody></table>

### Proxy Configuration

To route all SDK traffic through an HTTP or HTTPS proxy, set `proxy_url` on `ClientOptions` or use environment variables. The proxy applies to all SDK operations: API calls, file uploads, file downloads, thumbnail downloads, and caching requests.

{% tabs %}
{% tab title="Proxy via ClientOptions" %}

```rust
let mut client_options = ClientOptions::new_client_options(config);
client_options.proxy_url = Some("http://proxy.example.com:8080".to_string());
let mut secrets_manager = SecretsManager::new(client_options)?;
```

{% endtab %}

{% tab title="Authenticated Proxy Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main() -> Result<(), KSMRError> {
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let mut client_options = ClientOptions::new_client_options(config);

    // Authenticated proxy: http://user:pass@host:port
    client_options.proxy_url = Some("http://user:pass@proxy.example.com:8080".to_string());

    let mut secrets_manager = SecretsManager::new(client_options)?;
    let secrets = secrets_manager.get_secrets(Vec::new())?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Option                               | Description                                                                                                                                                                                                   |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ClientOptions.proxy_url`            | Set proxy programmatically. Takes precedence over environment variables.                                                                                                                                      |
| `HTTP_PROXY` / `HTTPS_PROXY`         | Standard proxy environment variables. Used when `proxy_url` is not set.                                                                                                                                       |
| `KSM_PROXY_URL`                      | Used by `caching_post_function` when a proxy is needed for cache requests. Not read by `make_caching_post_function`. Configure the proxy on the `reqwest::blocking::Client` you pass to that factory instead. |
| `ClientOptions.insecure_skip_verify` | Set to `true` to disable TLS certificate verification. Not recommended outside testing.                                                                                                                       |
| `KSM_SKIP_VERIFY`                    | Environment variable alternative to `insecure_skip_verify`. Set to `true` to skip verification, `false` (default) to require it.                                                                              |

### Retrieve Secrets

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

```rust
let records_filter = Vec::new(); // add record filters of needed based on UID
let secrets = secrets_manager.get_secrets(records_filter)?;
```

{% endtab %}

{% tab title="Example: Get All Secrets" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    custom_error::KSMRError,
    storage::FileKeyValueStorage
};

fn main()-> Result<(), KSMRError>{
    let token = "<Your One time token>".to_string();
    let file_name = FileKeyValueStorage::new_config_storage("test.json".to_string())?;
    let client_options = ClientOptions::new_client_options_with_token(token, file_name);
    
    let mut secrets_manager = SecretsManager::new(client_options)?;
    let secrets = secrets_manager.get_secrets(Vec::new())?;

    for secret in secrets {
        secret.print();
        println!("---");
    }
    Ok(())
}
```

{% endtab %}

{% tab title="Example: Get Secrets With a Filter" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    custom_error::KSMRError,
    storage::FileKeyValueStorage
};

fn main()-> Result<(), KSMRError>{
    let token = "<Your One time token>".to_string();
    let file_name = FileKeyValueStorage::new_config_storage("test.json".to_string())?;
    let client_options = ClientOptions::new_client_options_with_token(token, file_name);
    
    let mut secrets_manager = SecretsManager::new(client_options)?;
    let records_filter = vec!["record_uid1".to_string(), "record_uid2".to_string()]; // add record filters if needed based on UID
    let secrets = secrets_manager.get_secrets(records_filter)?;

    for secret in secrets {
        secret.print();
        println!("---");
    }
    Ok(())
}
```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="150">Parameter</th><th width="150">Type</th><th width="150">Required</th><th width="150">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>uids</code></td><td><code>Vec&#x3C;String></code></td><td>Yes</td><td><code>Vec::new()</code></td><td>Record UIDs to fetch. Pass an empty vec to fetch all records the application has access to.</td></tr></tbody></table>

**Returns**

> Type: `Result<Vec<Record>, KSMRError>`

All Keeper records, or records with the given UIDs

> default - we will get all records which the token given has access to

#### Get Secrets with Options

Use `get_secrets_with_options()` to request additional data such as GraphSync linked records.

{% tabs %}
{% tab title="Get Secrets with Options" %}

```rust
let query_options = QueryOptions::with_links(Vec::new(), Vec::new(), true);
let secrets = secrets_manager.get_secrets_with_options(query_options)?;
```

{% endtab %}

{% tab title="Example: Fetch Linked Records" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    dto::payload::QueryOptions,
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main() -> Result<(), KSMRError> {
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // Request linked records alongside the primary secrets
    // Vec::new() means no UID or folder filter — fetch all secrets
    let query_options = QueryOptions::with_links(Vec::new(), Vec::new(), true);
    let secrets = secrets_manager.get_secrets_with_options(query_options)?;

    for secret in &secrets {
        println!("Secret: {}", secret.title);
        if !secret.links.is_empty() {
            println!("  Linked records: {}", secret.links.len());         }
      }
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter       | Type           | Required | Description             |
| --------------- | -------------- | -------- | ----------------------- |
| `query_options` | `QueryOptions` | Yes      | Options for the request |

**QueryOptions Fields:**

| Field            | Type           | Description                                                                                                        |
| ---------------- | -------------- | ------------------------------------------------------------------------------------------------------------------ |
| `records_filter` | `Vec<String>`  | Record UIDs to fetch. Empty vec fetches all records.                                                               |
| `folders_filter` | `Vec<String>`  | Folder UIDs to filter by. Empty vec applies no folder filter.                                                      |
| `request_links`  | `Option<bool>` | Set to `true` to include GraphSync linked records in the response. Linked records are available on `Record.links`. |

**Returns**

> Type: `Result<Vec<Record>, KSMRError>`

All records matching the query options.

### Retrieve Values from Secret

#### Retrieve a password

This shortcut gets the password of a secret once that secret has been retrieved from Keeper Secrets Manager.

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

```rust
secret.get_standard_field_value("password", true);
```

{% endtab %}

{% tab title="Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by record UID
    let secrets = secrets_manager.get_secrets(vec!["record_uid".to_string()])?;
    let secret = match secrets.len(){
        0 => return Err(KSMRError::CustomError("no secret with given uid is found".to_string())),
        _ => &secrets[0],
    };
    // get password from record
    let my_secret_password = secret.get_standard_field_value("password", true)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

#### Retrieve Standard Fields

{% tabs %}
{% tab title="Standard Field" %}

```rust
secret.get_standard_field_value("FIELD_TYPE", true);
```

{% endtab %}

{% tab title="Standard Field Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::FileKeyValueStorage,
    custom_error::KSMRError,
    enums::StandardFieldTypeEnum
};

fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let token = "your_token_goes_here".to_string();
    let config = FileKeyValueStorage::new_config_storage("test.json".to_string())?;
    let client_options = ClientOptions::new_client_options_with_token(token, config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by record UID
    let secrets = secrets_manager.get_secrets(vec!["record_uid".to_string()])?;
    let secret = match secrets.len(){
        0 => return Err(KSMRError::CustomError("no secret with given uid is found".to_string())),
        _ => &secrets[0],
    };
    // use StandardFieldTypeEnum for getting accurate type for standard field without any typographic errors
    let login_field = StandardFieldTypeEnum::LOGIN.get_type();
    // get login field from the secret
    let my_secret_login = secret.get_standard_field_value(login_field, true)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter    | Type   | Required | Default | Description                 |
| ------------ | ------ | -------- | ------- | --------------------------- |
| `field_type` | `&str` | Yes      | None    | Field type to get           |
| `single`     | `bool` | Optional | `false` | Return only the first value |

**Returns**

> Type: `Result<Value, KSMRError>`

The field value as a JSON value. When `single = true`, returns the first value; otherwise returns an array.

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

#### Retrieve Custom Fields

{% tabs %}
{% tab title="Custom Field" %}

```rust
secret.get_custom_field_value(“FIELD_TYPE”, true);
```

{% endtab %}

{% tab title="Custom Field Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by record UID
    let secrets = secrets_manager.get_secrets(vec!["record_uid".to_string()])?;
    let secret = match secrets.len(){
        0 => return Err(KSMRError::CustomError("no secret with given uid is found".to_string())),
        _ => &secrets[0],
    };
    // Get a custom field, e.g. API Key
    let api_key = secret.get_custom_field_value("API Key", true)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter    | Type   | Required | Default | Description                 |
| ------------ | ------ | -------- | ------- | --------------------------- |
| `field_type` | `&str` | Yes      | -       | Field type to get           |
| `single`     | `bool` | Optional | `false` | Return only the first value |

Custom fields are any field that is not part of the record type definition but can be added by users.

**Returns**

> Type: `Result<Value, KSMRError>`

The field value as a JSON value. When `single = true`, returns the first value; otherwise returns an array

#### Search Secrets by Title

Search for secrets using their title instead of UID. The SDK provides two methods:

* `get_secrets_by_title()` - Returns all secrets matching the exact title
* `get_secret_by_title()` - Returns the first secret matching the exact title

{% tabs %}
{% tab title="Get All Secrets by Title" %}

```rust
secrets_manager.get_secrets_by_title(record_title)
```

{% endtab %}

{% tab title="Get All Secrets by Title Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get all secrets matching the record title
    let matching_secrets = secrets_manager.get_secrets_by_title("My Credentials")?;
    println!("Found {} matching secrets", matching_secrets.len());

    Ok(())
}
```

{% endtab %}

{% tab title="Get First Secret by Title" %}

```rust
secrets_manager.get_secret_by_title(record_title)
```

{% endtab %}

{% tab title="Get First Secret by Title Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get first secret matching the record title
    if let Some(secret) = secrets_manager.get_secret_by_title("My Credentials")? {
        println!("Found secret: {}", secret.uid);
    } else {
        println!("No secret found with that title");
    }

    Ok(())
}
```

{% endtab %}
{% endtabs %}

**Response**

<table><thead><tr><th width="217.11328125">Method</th><th width="307.4375">Return Type</th><th>Description</th></tr></thead><tbody><tr><td><code>get_secrets_by_title()</code></td><td><code>Result&#x3C;Vec&#x3C;Record>, KSMRError></code></td><td>All secrets matching the exact title</td></tr><tr><td><code>get_secret_by_title()</code></td><td><code>Result&#x3C;Option&#x3C;Record>, KSMRError></code></td><td>First secret matching title, or <code>None</code></td></tr></tbody></table>

| Parameter      | Type   | Required | Description                       |
| -------------- | ------ | -------- | --------------------------------- |
| `record_title` | `&str` | Yes      | Exact title of the record to find |

{% hint style="info" %}
Title matching is **case-sensitive** and requires an **exact match**. For partial or case-insensitive searching, retrieve all secrets and filter client-side.
{% endhint %}

#### Retrieve Values using Keeper Notation

{% tabs %}
{% tab title="get Notation" %}

```rust
secrets_manager.get_notation(query)
```

{% endtab %}

{% tab title="Notation Example" %}

<pre class="language-rust"><code class="lang-rust">use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result&#x3C;(), KSMRError>{
    // setup secrets manager
<strong>    let config_string = "your_base64_goes_here".to_string();
</strong>    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
<strong>    let mut secrets_manager = SecretsManager::new(client_options)?;
</strong>
    // get all secrets matching the notation
    let notation = "HDQTnxkTcPSOsHNAlbI4aQ/field/login".to_string();
    let result = secrets_manager.get_notation(notation)?;
    Ok(())
}
</code></pre>

{% endtab %}
{% endtabs %}

See [Keeper Notation documentation](/en/keeperpam/secrets-manager/about/keeper-notation.md) to learn about Keeper Notation format and capabilities

| Parameter | Type     | Required | Default | Description                                                      |
| --------- | -------- | -------- | ------- | ---------------------------------------------------------------- |
| `query`   | `String` | Yes      | -       | Keeper Notation query for getting a value from a specified field |

**Returns**

> Type: `Result<serde_json::Value, KSMRError>`

The value of the queried field as a JSON value. When the field contains a single value it resolves to a JSON string; multi-value fields resolve to a JSON array.

#### Retrieve a TOTP Code

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

```rust
get_totp_code(&url)
```

{% endtab %}

{% tab title="Example Usage" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    enums::StandardFieldTypeEnum,
    utils
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let record = &secrets[0];

    // get TOTP url value from a record
    let value = record.get_standard_field_value(StandardFieldTypeEnum::ONETIMECODE.get_type(), false)?;
    let url: String = utils::get_otp_url_from_value_obj(value)?;

    // get code from TOTP url
    let totp = utils::get_totp_code(&url)?;
    println!("{}", totp.get_code());
    Ok(())
}
```

{% endtab %}
{% endtabs %}

**Returns**

> Type: `Result<TotpCode,KSMRError>`

| Parameter | Type   | Required | Description |
| --------- | ------ | -------- | ----------- |
| `url`     | `&str` | Yes      | TOTP Url    |

### 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 %}

#### **Update Secret**

The `update_secret()` method is the standard way to update records.

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

```rust
secrets_manager.update_secret(record)
```

{% endtab %}

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

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    enums::StandardFieldTypeEnum
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let mut secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let mut secret_to_update = secrets.into_iter().next()
        .ok_or_else(|| KSMRError::CustomError("no secret found with given UID".to_string()))?;

    // update a field value
    let field_type = StandardFieldTypeEnum::LOGIN.get_type();
    secret_to_update.set_standard_field_value_mut(field_type, "sample@ks.com".into())?;

    // save the changes
    secrets_manager.update_secret(secret_to_update)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter | Type     | Required | Default | Description                      |
| --------- | -------- | -------- | ------- | -------------------------------- |
| `record`  | `Record` | Yes      |         | Record with updated field values |

**Response**

Returns `Result<(), KSMRError>` - success or error

#### **Password Rotation with Transactions**

The `update_secret_with_transaction()` method enables password rotation workflows where you can test the new password before committing the change.

{% hint style="info" %}
The `rollback` parameter follows its name: pass `true` to roll back the rotation, `false` to commit it. Pass `false` to finalize the new password.
{% endhint %}

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

```rust
secrets_manager.update_secret_with_transaction(record, UpdateTransactionType::Rotation)?;
// Test the new password...
secrets_manager.complete_transaction(record_uid, false)?; // false = commit, true = rollback
```

{% endtab %}

{% tab title="Password Rotation Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    enums::StandardFieldTypeEnum,
    dto::payload::UpdateTransactionType
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let mut secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let mut secret_to_update = secrets.into_iter().next()
        .ok_or_else(|| KSMRError::CustomError("no secret found with given UID".to_string()))?;
    let record_uid = secret_to_update.uid.clone();

    // update password field
    let field_type = StandardFieldTypeEnum::PASSWORD.get_type();
    secret_to_update.set_standard_field_value_mut(field_type, "NewRotatedPassword123!".into())?;

    // start rotation transaction
    secrets_manager.update_secret_with_transaction(secret_to_update, UpdateTransactionType::Rotation)?;

    // Test the new password in your application...
    // If successful:
    secrets_manager.complete_transaction(record_uid, false)?; // commit

    // If testing failed:
    // secrets_manager.complete_transaction(record_uid, true)?; // rollback

    Ok(())
}
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th>Parameter</th><th width="248.54296875">Type</th><th width="135.26953125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>record</code></td><td><code>Record</code></td><td>Yes</td><td>Record with updated field values</td></tr><tr><td><code>transaction_type</code></td><td><code>UpdateTransactionType</code></td><td>Yes</td><td><code>General</code> or <code>Rotation</code></td></tr><tr><td><code>record_uid</code></td><td><code>String</code></td><td>Yes</td><td>UID of the record to finalize transaction for</td></tr><tr><td><code>rollback</code></td><td><code>bool</code></td><td>Yes</td><td><code>false</code> to commit, <code>true</code> to rollback</td></tr></tbody></table>

#### **Remove File Links**

The `update_secret_with_options()` method provides fine-grained control over updates, including the ability to remove file links.

{% tabs %}
{% tab title="Update with Options" %}

```rust
let update_options = UpdateOptions::new(
    UpdateTransactionType::General,
    vec!["file-uid-to-remove".to_string()]
);
secrets_manager.update_secret_with_options(record, update_options)?;
```

{% endtab %}

{% tab title="Update with Options Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    dto::payload::{UpdateOptions, UpdateTransactionType}
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let secret_to_update = secrets.into_iter().next()
        .ok_or_else(|| KSMRError::CustomError("no secret found with given UID".to_string()))?;

    // Modify fields as needed...

    // Create update options to remove file links
    let update_options = UpdateOptions::new(
        UpdateTransactionType::General,
        vec!["file-uid-to-remove-1".to_string(), "file-uid-to-remove-2".to_string()]
    );

    // Update the record and remove specified file links
    secrets_manager.update_secret_with_options(secret_to_update, update_options)?;

    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter        | Type            | Required | Default | Description                                |
| ---------------- | --------------- | -------- | ------- | ------------------------------------------ |
| `record`         | `Record`        | Yes      |         | Record with updated field values           |
| `update_options` | `UpdateOptions` | Yes      |         | Configuration for advanced update behavior |

**UpdateOptions Fields:**

| Field              | Type                    | Description                        |
| ------------------ | ----------------------- | ---------------------------------- |
| `transaction_type` | `UpdateTransactionType` | `General` or `Rotation`            |
| `links_to_remove`  | `Vec<String>`           | UIDs of file attachments to remove |

Set field values using the `set_standard_field_value_mut` or the `set_custom_field_value_mut` method.

Fields are found by type.

For a list of field types, see the [Record Types](/en/enterprise-guide/record-types.md) documentation. Some fields have multiple values in these cases, the value can be set to a list.

#### Generate a Random Password

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

```rust
generate_password_with_options(password_options);
```

{% endtab %}

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

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    enums::StandardFieldTypeEnum,
    utils::{generate_password_with_options, PasswordOptions}
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let mut secret = secrets.into_iter().next()
        .ok_or_else(|| KSMRError::CustomError("no secret found with given UID".to_string()))?;

    // generate a random password
    let charset: String = "$_!?#".to_string();
    let length = 32;
    let digits = 2;
    let lowercase = 2;
    let uppercase = 2;
    let special_characters = 2;
    let password_options = PasswordOptions::new()
        .length(length)
        .digits(digits)
        .lowercase(lowercase)
        .uppercase(uppercase)
        .special_characters(special_characters)
        .special_characterset(charset);
    let password = generate_password_with_options(password_options)?;

    // update record with new password
    let field_type = StandardFieldTypeEnum::PASSWORD.get_type();
    secret.set_standard_field_value_mut(field_type, password.into())?;

    // Save changes to the secret
    secrets_manager.update_secret(secret)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="185.8203125">Parameter</th><th width="157.40234375">Type</th><th>Required</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>password_options</code></td><td><code>PasswordOptions</code></td><td>Yes</td><td></td><td>Configuration for the password</td></tr><tr><td><code>special_characterset</code></td><td><code>String</code></td><td>Optional</td><td><code>"""!@#$%()+;&#x3C;>=?[]{}^.,"""</code></td><td>Set of special characters to be included in the password</td></tr><tr><td><code>length</code></td><td><code>usize</code></td><td>Optional</td><td>32</td><td>Length of password</td></tr><tr><td><code>lowercase</code></td><td><code>i32</code></td><td>Optional</td><td>None</td><td>Count of lowercase characters in the password</td></tr><tr><td><code>uppercase</code></td><td><code>i32</code></td><td>Optional</td><td>None</td><td>Count of uppercase characters in the password</td></tr><tr><td><code>digits</code></td><td><code>i32</code></td><td>Optional</td><td>None</td><td>Count of digits in the password</td></tr><tr><td><code>special_characters</code></td><td><code>i32</code></td><td>Optional</td><td>None</td><td>Count of special characters in the password</td></tr></tbody></table>

By default, each count parameter specifies the **minimum** number of that character type. Pass a negative value to specify an **exact** count instead — for example, `.lowercase(-8)` produces exactly 8 lowercase characters. When all count parameters are negative or zero (exact mode), the `length` parameter is ignored and the total password length is derived from the sum of the absolute values.

### Download a File

{% tabs %}
{% tab title="Download File" %}

```rust
secret.download_file(file_name, path);
```

{% endtab %}

{% tab title="Download File example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let secret = &secrets[0];

    // Download file to a specific path
    let path = format!("./temp/demo_{}.txt", secret.title);
    secret.download_file("uploaded_file.txt", &path)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter   | Type   | Required | Description                                 |
| ----------- | ------ | -------- | ------------------------------------------- |
| `file_name` | `&str` | Yes      | Name, title, or UID of the file to download |
| `path`      | `&str` | Yes      | Path to download file                       |

**Returns**

> Type: `Result<bool, KSMRError>`

`true` if the file was found and saved to `path`; `false` if no file with that name, title, or UID exists on the record.

#### Download File by Title

Downloads a file attachment by record title and filename. The SDK searches all accessible records for one matching `record_title`, then retrieves the file named `file_name` from that record.

{% tabs %}
{% tab title="Download File by Title" %}

```rust
secrets_manager.download_file_by_title(record_title, file_name)
```

{% endtab %}

{% tab title="Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main() -> Result<(), KSMRError> {
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // Download a file by record title and file name
    if let Some(file_data) = secrets_manager.download_file_by_title("My Record Title", "report.pdf")? {
        std::fs::write("./report.pdf", &file_data)
            .map_err(|e| KSMRError::CustomError(e.to_string()))?;
        println!("File downloaded successfully");
    } else {
        println!("Record or file not found");
    }
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter      | Type   | Required | Description                             |
| -------------- | ------ | -------- | --------------------------------------- |
| `record_title` | `&str` | Yes      | Title of the record containing the file |
| `file_name`    | `&str` | Yes      | Name of the file attachment to download |

**Returns**

> Type: `Result<Option<Vec<u8>>, KSMRError>`

* `Ok(Some(bytes))` — decrypted file data as raw bytes
* `Ok(None)` — no record with that title, or no file with that name was found
* `Err(...)` — error during download or decryption

#### Download File Thumbnail

Some file attachments (especially images) have thumbnail previews. You can download these thumbnails separately from the full file.

{% tabs %}
{% tab title="Download Thumbnail" %}

```rust
file.get_thumbnail_data()
```

{% endtab %}

{% tab title="Download Thumbnail Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let secret = &secrets[0];

    // Download thumbnails for all files
    for mut file in secret.files.clone() {
        if let Some(thumbnail_data) = file.get_thumbnail_data()? {
            let thumbnail_path = format!("./temp/{}_thumb.jpg", file.name);
            std::fs::write(&thumbnail_path, &thumbnail_data)
                .map_err(|e| KSMRError::FileWriteError(thumbnail_path.clone(), e))?;
            println!("Downloaded thumbnail: {}", thumbnail_path);
        } else {
            println!("No thumbnail available for {}", file.name);
        }
    }

    Ok(())
}
```

{% endtab %}
{% endtabs %}

**Returns**

> Type: `Result<Option<Vec<u8>>, KSMRError>`

* `Ok(Some(bytes))` - Thumbnail data (typically JPEG)
* `Ok(None)` - No thumbnail available for this file
* `Err(...)` - Error downloading or decrypting thumbnail

{% hint style="info" %}
Thumbnails are typically available for image files (PNG, JPEG, etc.). Document files and other non-image types usually do not have thumbnails.
{% endhint %}

### Upload File

{% tabs %}
{% tab title="Upload File" %}

```rust
upload_file(owner_record, keeper_file);
```

{% endtab %}

{% tab title="Upload File Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    dto::file::KeeperFileUpload
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // get a specific secret by UID
    let secrets = secrets_manager.get_secrets(vec!["<RECORD UID>".to_string()])?;
    let owner_record = &secrets[0];

    // Prepare file data for upload
    let file_path = "./local_file.txt";
    let file_name = Some("uploaded_file.txt");
    let file_title = Some("My File");
    let mime_type = Some("text/plain");
    let keeper_file = KeeperFileUpload::get_file_for_upload(
        file_path,
        file_name,
        file_title,
        mime_type
    )?;

    // Upload file attached to the owner record and get the file UID
    let file_uid = secrets_manager.upload_file(owner_record, keeper_file)?;
    println!("Uploaded file UID: {}", file_uid);
    Ok(())
}
```

{% endtab %}
{% endtabs %}

**Upload File Parameters**

<table><thead><tr><th>Parameter</th><th width="162.203125">Type</th><th>Required</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>owner_record</code></td><td><code>Record</code></td><td>Yes</td><td>None</td><td>The record in which the file has to be uploaded</td></tr><tr><td><code>keeper_file</code></td><td><code>KeeperFileUpload</code></td><td>Yes</td><td></td><td>The file to be uploaded</td></tr></tbody></table>

**File Parameters**

| Parameter    | Type           | Required | Default | Description                                                                                |
| ------------ | -------------- | -------- | ------- | ------------------------------------------------------------------------------------------ |
| `file_path`  | `&str`         | Yes      |         | Path to upload file                                                                        |
| `file_name`  | `Option<&str>` | Yes      |         | Name of the file to be uploaded                                                            |
| `file_title` | `Option<&str>` | Yes      |         | Title of the file to be uploaded                                                           |
| `mime_type`  | `Option<&str>` | Yes      | None    | The type of data in the file. If none is provided, 'application/octet-stream' will be used |

**Returns**

> Type: `Result<String, KSMRError>`

The file UID of the attached file

### Create a Secret

**Prerequisites:**

* Shared folder UID
  * The 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](/en/keeperpam/commander-cli/command-reference/record-commands/creating-and-updating-records.md#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 [upload\_file](/en/keeperpam/secrets-manager/developer-sdk-library/python-sdk.md#upload-a-file)

> **Multi-value fields**: To assign multiple values to a single field (e.g. two phone numbers, two security questions), pass a JSON array directly to `RecordField::new_record_field`. Single objects are still wrapped automatically; only pass an array when you have more than one entry.

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

```rust
secrets_manager.create_secret(folder_uid, record);
```

{% endtab %}

{% tab title="Login record Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    dto::{dtos::RecordCreate, field_structs::RecordField},
    utils
};
use serde_json::{self, json, Number, Value};

fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // This is how we create a Record
    let mut created_record =  RecordCreate::new("login".to_string(), "Login Record RUST_LOG_TEST".to_string(), Some("Dummy Notes".to_string()));
    
    // This is how we create a single field 
    let password_field = RecordField::new_record_field_with_options("password", Value::String(utils::generate_password()?), Some("Random password label".to_string()), false, true);

    //This is one way to create all fields directly in a vector
    let fields = vec![
        RecordField::new_record_field("login", Value::String("login@email.com".to_string()), Some("My Custom Login lbl".to_string())),

        password_field,
     ];

    created_record.fields = Some(fields);
  
    // Make the API call
    let _ = secrets_manager.create_secret("Shared_folder_uid".to_string(), created_record)?;
    Ok(())
}
```

{% endtab %}

{% tab title="Custom Type Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    dto::{dtos::RecordCreate, field_structs::RecordField},
    utils
};
use serde_json::{self, json, Number, Value};

fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // This is how we create a Record
    let mut created_record = RecordCreate::new("login".to_string(), "Login Record RUST_LOG_TEST".to_string(), Some("Dummy Notes".to_string()));

    // This is how we create a single field
    let password_field = RecordField::new_record_field_with_options("password", Value::String(utils::generate_password()?), Some("Random password label".to_string()), false, true);

    // This is one way to create all fields directly in a vector
    let fields = vec![
        RecordField::new_record_field("login", Value::String("login@email.com".to_string()), Some("My Custom Login lbl".to_string())),

        RecordField::new_record_field("login", Value::String("login@email.com".to_string()), Some("My Label".to_string())),

        password_field,

        // Single security question (pass an object for one entry)
        RecordField::new_record_field("securityQuestion", json!({"question": "What is the question?", "answer": "This is the answer!"}), Some("My Security Question".to_string())),

        // Multiple security questions (pass an array for more than one entry)
        RecordField::new_record_field("securityQuestion", json!([
            {"question": "Childhood pet?", "answer": "Fluffy"},
            {"question": "City of birth?", "answer": "Chicago"},
        ]), Some("My Security Questions".to_string())),

        RecordField::new_record_field("multiline", Value::String("This\nIs a multiline\nnote".to_string()), Some("My Multiline lbl".to_string())),

        RecordField::new_record_field("secret", Value::String("SecretText".to_string()), Some("My Hidden Field lbl".to_string())),

        RecordField::new_record_field("pinCode", Value::String("1234567890".to_string()), Some("My Pin Code Field Lbl".to_string())),

        RecordField::new_record_field("addressRef", Value::String("some_UID".to_string()), Some("My Address Reference".to_string())),

        // Single phone number
        RecordField::new_record_field("phone", json!({"region": "US", "number": "510-444-3333"}), Some("My Phone Number".to_string())),

        // Multiple phone numbers (pass an array for more than one entry)
        RecordField::new_record_field("phone", json!([
            {"region": "US", "number": "510-444-3333", "type": "Mobile"},
            {"region": "US", "number": "510-555-6666", "type": "Work"},
        ]), Some("My Phone Numbers".to_string())),

        RecordField::new_record_field("date", Value::Number(Number::from(1641934793000i64)), Some("My date".to_string())),

        RecordField::new_record_field("name", json!({"first": "Lincoln", "last": "Adams"}), Some("His Name".to_string())),
    ];

    // Here we are adding fields object to standard fields
    created_record.fields = Some(fields);

    created_record.custom = Some(
        vec![
            RecordField::new_record_field("phone", json!({"region": "US", "number": "510-222-5555", "ext": "99887", "type": "Mobile"}), Some("My Custom Phone Lbl".to_string())),
        ]
    );

    // Make the API call
    let _ = secrets_manager.create_secret("Shared_folder_uid".to_string(), created_record)?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter              | Type           | Required | Description                                      |
| ---------------------- | -------------- | -------- | ------------------------------------------------ |
| `parent_folder_uid`    | `String`       | Yes      | UID of the shared folder to create the record in |
| `record_create_object` | `RecordCreate` | Yes      | Record definition. Type, title, and fields       |

**RecordCreate fields:**

<table><thead><tr><th width="142.94140625">Field</th><th width="170.390625">Type</th><th width="116.734375">Required</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>record_type</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Record type (e.g. <code>"login"</code>)</td></tr><tr><td><code>title</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Title of the record</td></tr><tr><td><code>notes</code></td><td><code>Option&#x3C;String></code></td><td>No</td><td>None</td><td>Optional note on the record</td></tr><tr><td><code>fields</code></td><td><code>Vec&#x3C;RecordField></code></td><td>No</td><td>None</td><td>Standard fields</td></tr><tr><td><code>custom</code></td><td><code>Vec&#x3C;RecordField></code></td><td>No</td><td>None</td><td>Custom fields</td></tr></tbody></table>

**Returns**

> Type: `Result<String, KSMRError>`

The record UID of the new record

### Delete A Secret

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

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

```rust
secrets_manager.delete_secret(vec![record_uid]);
```

{% endtab %}

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

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // delete a specific secret by UID
    let secret_to_delete = secrets_manager.delete_secret(vec!["<RECORD UID>".to_string()])?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="121.6927490234375">Parameter</th><th width="122.708251953125">Type</th><th width="107.692626953125">Required</th><th width="95.7239990234375">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>record_uid</code></td><td><code>Vec&#x3C;String></code></td><td>Yes</td><td>None</td><td>The uid of the record to be deleted</td></tr></tbody></table>

**Returns**

> Type: `Result<String, KSMRError>`

The server response string on success.

### Caching

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

#### Setup and Configure Cache

The Rust SDK provides `caching::make_caching_post_function(client)` for disaster recovery caching. It stores the last successful API response to a local encrypted file and falls back to that data automatically when the server is unreachable. Build the `reqwest::blocking::Client` once outside any async context (it is safe to use inside `tokio::task::spawn_blocking`), then pass it to the factory. As long as there is network connectivity, the SDK always prefers a live response over cached data.

{% tabs %}
{% tab title="Setup Caching" %}

```rust
use keeper_secrets_manager_core::caching;

// Build the client once outside any async context — safe under tokio::spawn_blocking
let http_client = reqwest::blocking::Client::builder().build()?;
let mut client_options = ClientOptions::new_client_options_with_token(token, config);
client_options.set_custom_post_function(caching::make_caching_post_function(http_client));
let mut secrets_manager = SecretsManager::new(client_options)?;
```

{% endtab %}

{% tab title="Caching Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::FileKeyValueStorage,
    custom_error::KSMRError,
    caching
};
fn main()-> Result<(), KSMRError>{
    let token = "<Your One time token>".to_string();
    let file_name = FileKeyValueStorage::new_config_storage("test.json".to_string())?;

    let mut client_options = ClientOptions::new_client_options_with_token(token, file_name);

    // Build the client once outside any async context — safe under tokio::spawn_blocking
    let http_client = reqwest::blocking::Client::builder()
        .build()
        .map_err(|e| KSMRError::HTTPError(e.to_string()))?;
    client_options.set_custom_post_function(caching::make_caching_post_function(http_client));

    let mut secrets_manager = SecretsManager::new(client_options)?;

    // First successful call saves to cache (default: KSM_CACHE_DIR/ksm_cache.bin)
    let secrets = secrets_manager.get_secrets(Vec::new())?;

    // Subsequent calls automatically fall back to cache if network fails
    println!("Retrieved {} secrets (with automatic cache fallback)", secrets.len());

    Ok(())
}
```

{% endtab %}
{% endtabs %}

**Cache Location:**

* Default: `$KSM_CACHE_DIR/ksm_cache.bin`
* If `KSM_CACHE_DIR` not set, uses system temp directory
* Override with environment variable: `export KSM_CACHE_DIR=/path/to/cache`

{% hint style="danger" %}
The default caching function stores only the **last successful request**. For example, if the first request (R1) successfully retrieves UID1 and updates the cache, but a subsequent request (R2) for UID2 fails, the cache will not include UID2. As a result, any later operations involving UID2 will return an empty response if network is down, since it was never added to the cache.
{% endhint %}

{% hint style="warning" %}
Updating a record from cache (or creating new record) invalidates cached record data and consecutive updates of the same record will fail. Batch updates work as long as they modify different records. Always follow up cached record updates with a call to `get_secrets()` to refresh cache (and pull updated metadata from vault like the new record revision).
{% endhint %}

#### Custom Cache Implementations

For advanced use cases requiring custom cache management (e.g., preferring local cache over network, custom refresh intervals), you can implement your own caching logic using the `KSMRCache` as a starting point.

{% tabs %}
{% tab title="Custom Cache Setup" %}

```rust
use keeper_secrets_manager_core::cache::KSMRCache;

let cache = KSMRCache::new_file_cache(Some("./cache.bin"))?;
let mut client_options = ClientOptions::new_client_options_with_token(token, config);
client_options.set_cache(cache.into());
let mut secrets_manager = SecretsManager::new(client_options)?;
```

{% endtab %}

{% tab title="Custom Cache Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    custom_error::KSMRError,
    storage::FileKeyValueStorage,
    cache::KSMRCache
};
fn main()-> Result<(), KSMRError>{
    let cache = KSMRCache::new_file_cache(Some("./cache.bin"))?;
    let token = "<Token>".to_string();
    let file_name = FileKeyValueStorage::new_config_storage("test.json".to_string())?;

    let mut client_options = ClientOptions::new_client_options_with_token(token, file_name);
    client_options.set_cache(cache.into());

    let mut secrets_manager = SecretsManager::new(client_options)?;
    let secrets = secrets_manager.get_secrets(Vec::new())?;

    for secret in secrets {
        println!("Secret: {}", secret.title);
    }

    Ok(())
}
```

{% endtab %}
{% endtabs %}

### Folders

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

### Read Folders

Downloads full folder hierarchy.

{% tabs %}
{% tab title="Read Folders" %}

```rust
secrets_manager.get_folders()
```

{% endtab %}

{% tab title="Read Folders Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;
    
    // get all folder
    let folders = secrets_manager.get_folders()?;
    Ok(())
}
```

{% endtab %}
{% endtabs %}

**Returns**

> Type: `Result<Vec<KeeperFolder>, KSMRError>`

### Create Folder

Requires `CreateOptions` and folder name to be provided. The folder UID parameter in `CreateOptions` is required—the UID of a shared folder, while sub-folder UID is optional, and if missing, a 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.

{% tabs %}
{% tab title="Create Folder" %}

```rust
secrets_manager.create_folder(create_options, folder_name, folders)
```

{% endtab %}

{% tab title="Create Folder Under Shared Folder" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    dto::payload::CreateOptions
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // Create a folder directly under the shared folder
    let parent_folder_uid = "<PARENT_SHARED_FOLDER_UID>".to_string();
    let create_options = CreateOptions::new(parent_folder_uid, None);
    let new_folder_name = "My New Folder".to_string();

    let folder_uid = secrets_manager.create_folder(create_options, new_folder_name, Vec::new())?;
    println!("Created folder UID: {}", folder_uid);
    Ok(())
}
```

{% endtab %}

{% tab title="Create Nested Subfolder" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError,
    dto::payload::CreateOptions
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    // Create a subfolder within an existing folder
    let parent_folder_uid = "<PARENT_SHARED_FOLDER_UID>".to_string();
    let sub_folder_uid = Some("<EXISTING_FOLDER_UID>".to_string());
    let create_options = CreateOptions::new(parent_folder_uid, sub_folder_uid);
    let new_folder_name = "Nested Subfolder".to_string();

    let folder_uid = secrets_manager.create_folder(create_options, new_folder_name, Vec::new())?;
    println!("Created subfolder UID: {}", folder_uid);
    Ok(())
}
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="146.0677490234375">Parameter</th><th width="179.9166259765625">Type</th><th width="105.2344970703125">Required</th><th width="97.6875">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>create_options</code></td><td><code>CreateOptions</code></td><td>Yes</td><td>None</td><td>The parent and sub-folder UIDs</td></tr><tr><td><code>folder_name</code></td><td><code>String</code></td><td>Yes</td><td></td><td>The folder name</td></tr><tr><td><code>folders</code></td><td><code>Vec&#x3C;KeeperFolder></code></td><td>No</td><td>None</td><td>List of folders to use in the search for parent and sub-folder from CreateOptions</td></tr></tbody></table>

**Returns**

> Type: `Result<String, KSMRError>`

The UID of the newly created folder.

### Update Folder

Updates the folder metadata—currently folder name only.

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

```rust
secrets_manager.update_folder(folder_uid, folder_name, folders)
```

{% endtab %}

{% tab title="Update Folder Example" %}

```rust
use keeper_secrets_manager_core::{
    core::{ClientOptions, SecretsManager},
    storage::InMemoryKeyValueStorage,
    custom_error::KSMRError
};
fn main()-> Result<(), KSMRError>{
    // setup secrets manager
    let config_string = "your_base64_goes_here".to_string();
    let config = InMemoryKeyValueStorage::new_config_storage(Some(config_string))?;
    let client_options = ClientOptions::new_client_options(config);
    let mut secrets_manager = SecretsManager::new(client_options)?;

    let update_folder = secrets_manager.update_folder("<folder_uid>".to_string(),"dummy_updated_API_RUST".to_string(),Vec::new())?;
    println!("{}",(serde_json::to_string_pretty(&update_folder)?));
    Ok(())
}
```

{% endtab %}
{% endtabs %}

| Parameter     | Type                | Required | Default | Description                                            |
| ------------- | ------------------- | -------- | ------- | ------------------------------------------------------ |
| `folder_uid`  | `String`            | Yes      |         | The folder uid                                         |
| `folder_name` | `String`            | Yes      |         | The new folder name                                    |
| `folders`     | `Vec<KeeperFolder>` | No       | None    | List of folders to use in the search for parent folder |

**Returns**

> Type: `Result<String, KSMRError>`

The server response string on success.

### Delete Folders

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

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

{% hint style="info" %}
When using `force_delete` 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 %}

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

```rust
secrets_manager.delete_folder(vec!["<FOLDER_UID>".to_string()], false)?
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th>Parameter</th><th>Type</th><th>Required</th><th width="106.2265625">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>folder_uids</code></td><td><code>Vec&#x3C;String></code></td><td>Yes</td><td></td><td>The folder UID list</td></tr><tr><td><code>force_delete</code></td><td><code>bool</code></td><td>No</td><td><code>false</code></td><td>Set to <code>true</code> to delete non-empty folders. Default <code>false</code> returns an error if the folder contains records.</td></tr></tbody></table>

**Returns**

> Type: `Result<Vec<HashMap<String, Value>>, KSMRError>`

A list of response entries for each deleted folder. On success the list is typically empty.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeper.io/en/keeperpam/secrets-manager/developer-sdk-library/rust-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
