# Python SDK

<figure><img src="https://762006384-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJXOXEifAmpyvNVL1to%2Fuploads%2Fq5NweawmL7egYFUPwVGz%2FKSM%20%2B%20python.png?alt=media&#x26;token=f444ebf4-73be-42a6-bbd9-ce8e5938f89f" alt=""><figcaption></figcaption></figure>

## Download and Installation

### Install with PIP

```bash
pip3 install -U keeper-secrets-manager-core
```

**Python Requirements**: Python 3.9 or higher

For more information, see <https://pypi.org/project/keeper-secrets-manager-core/>

### Source Code

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

## Using the SDK

### Initialize

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

```python
SecretsManager(token, config)
# Using token, only to generate a config (for later usage),
# requires at least one access operation to bind the token
#get_secrets(uids=None)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)
# Using token only to generate the config 
# requires at least one access operation to bind the token
#secrets_manager.get_secrets(uids=None)
```

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

{% hint style="info" %}
Configuration files are automatically created with secure permissions (0600 on Unix-based systems) in v17.1.0+.
{% endhint %}

### Retrieve Secrets

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

```python
get_secrets(uids=None)
```

{% endtab %}

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

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get all records
all_secrets = secrets_manager.get_secrets()

# print out all records
for secret in all_secrets:
    print(secret.dict)
```

{% endtab %}

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

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by record UID
secret = secrets_manager.get_secrets(['EG6KdJaaLG7esRZbMnfbFA'])[0]

# print out secret
print(secret.dict)
```

{% 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>String[]</code></td><td>Optional</td><td>None</td><td>Record UIDs to fetch</td></tr></tbody></table>

**Response**

Type: **`Record[]`**

All Keeper records, or records with the given UIDs

### Retrieve Values From a 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" %}

```python
secret.field('password', single=True)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by record UID
secret = secrets_manager.get_secrets(['<RECORD UID>'])[0]

# get password from record
my_secret_password = secret.field('password', single=True)
```

{% endtab %}
{% endtabs %}

#### Retrieve Standard Fields

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

```python
secret.field(field_type, value=None, single=False)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by record UID
secret = secrets_manager.get_secrets(['<RECORD UID>'])[0]

# get login field from the secret
my_secret_login = secret.field("login", single=True)
```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="168">Parameter</th><th width="150">Type</th><th>Required</th><th width="202">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>field_type</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Field type to get</td></tr><tr><td><code>value</code></td><td><code>String</code> or <code>String[]</code></td><td>Optional</td><td>None</td><td>If passed, set the value of the field to the given value</td></tr><tr><td><code>single</code></td><td><code>boolean</code></td><td>Optional</td><td>False</td><td>Return only the first value</td></tr></tbody></table>

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 Custom Fields

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

```python
secret.custom_field(label, field_type=None, value=None, single=False)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by UID
secret = secrets_manager.get_secrets(['EG6KdJaaLG7esRZbMnfbFA'])[0]

# Get a standard template field
password = secret.field('password', single=True)

# Get a custom field, e.g. API Key
api_key = secret.custom_field('API Key', single=True)
```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th>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>label</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Label of the custom field</td></tr><tr><td><code>field_type</code></td><td><code>String</code></td><td>Optional</td><td>None</td><td>Field type to get</td></tr><tr><td><code>value</code></td><td><code>String</code> or <code>String[]</code></td><td>Optional</td><td>None</td><td>If passed, set the value of the field to the given value</td></tr><tr><td><code>single</code></td><td><code>boolean</code></td><td>Optional</td><td>False</td><td>Return only the first value</td></tr></tbody></table>

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

It is possible for multiple fields of the same custom type to appear on a single record, to differentiate these fields, the field label is required.

**Response**

Type: **`String`** or **`String[]`**

the value or values of the field. Will be a single value only if the `single=True` option is passed.

#### Retrieve Secrets by Title

{% tabs %}
{% tab title="Records by Title" %}

```python
# get all matching records
get_secrets_by_title(record_title)

# get only the first matching record
get_secret_by_title(record_title)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    config=FileKeyValueStorage('ksm-config.json'))

# get the first secret matching the record title
secret = secrets_manager.get_secret_by_title("My Credentials")

# get all secrets matching the record title
secrets = secrets_manager.get_secrets_by_title("My Credentials")
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="178.10385756676556">Parameter</th><th width="226.46137991694417">Type</th><th width="150">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>record_title</code></td><td><code>String</code></td><td>Yes</td><td>Record title to search for</td></tr></tbody></table>

#### Access Linked Records (GraphSync)

Records in Keeper can be linked to other records using GraphSync. The Python SDK provides access to these links through the `links` property on records.

{% tabs %}
{% tab title="Access Links" %}

```python
# Access links from a record
for link in secret.links:
    print(f"Linked Record UID: {link['recordUid']}")
    if link.get('data'):
        print(f"Link Data: {link['data']}")
    if link.get('path'):
        print(f"Link Path: {link['path']}")
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manager
secrets_manager = SecretsManager(
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by UID
secret = secrets_manager.get_secrets(['<RECORD UID>'])[0]

# check if record has links
if secret.links:
    print(f"Record has {len(secret.links)} linked record(s)")

    # iterate through links
    for link in secret.links:
        linked_uid = link.get('recordUid')
        print(f"Linked to record: {linked_uid}")

        # optionally retrieve the linked record
        linked_record = secrets_manager.get_secrets([linked_uid])[0]
        print(f"Linked record title: {linked_record.title}")
else:
    print("Record has no links")
```

{% endtab %}
{% endtabs %}

**Link Object Structure**

Each link object contains:

<table><thead><tr><th width="150">Property</th><th width="150">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>recordUid</code></td><td><code>String</code></td><td>UID of the linked record</td></tr><tr><td><code>data</code></td><td><code>String</code> (optional)</td><td>Base64-encoded link data</td></tr><tr><td><code>path</code></td><td><code>String</code> (optional)</td><td>Path information for the link</td></tr></tbody></table>

{% hint style="info" %}
GraphSync links allow records to reference other records in your vault. This is useful for creating relationships between credentials, such as linking a database password to the database server configuration.
{% endhint %}

#### Retrieve Values using Keeper Notation

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

```python
get_notation(query)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific standard field with Keeper Notation
password = secrets_manager.get_notation('EG6KdJaaLG7esRZbMnfbFA/field/password')[0]

# get a specific custom field with Keeper Notation
custom_field_value = secrets_manager.get_notation('EG6KdJaaLG7esRZbMnfbFA/custom_field/my_field')
```

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

<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>query</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Keeper Notation query for getting a value from a specified field</td></tr></tbody></table>

#### Returns

Type: `string` or `string[]`

The value of the queried field

#### Retrieve a TOTP Code

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

```python
get_totp_code(url)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage
from keeper_secrets_manager_core.utils import get_totp_code

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get TOTP url value from a record
url = record.get_standard_field_value('oneTimeCode', True)

# get code from TOTP url
totp = get_totp_code(url)
print(totp.code)
```

{% 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>url</code></td><td><code>String</code></td><td>Yes</td><td></td><td>TOTP Url</td></tr></tbody></table>

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

**Save Changes to a Secret**

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

```python
save(record: KeeperRecord)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by UID
secret_to_update = secrets_manager.get_secrets(['EG6KdJaaLG7esRZbMnfbFA'])[0]

# update a field value
secret_to_update.field('login', value='new login')

# update non-field values
secret_to_update.title = "New Title"
secret_to_update.notes = "New Notes"

# save updated secret
secrets_manager.save(secret_to_update)
```

{% endtab %}

{% tab title="Transactional updates" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
     config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by UID
secret = secrets_manager.get_secrets(['EG6KdJaaLG7esRZbMnfbFA'])[0]

# rotate password on the record
secret.field('password', value='new password')
# start a transaction
secrets_manager.save(secret, 'rotation')
# rotate password on remote host
success = rotate_remote_ssh_password('new password')
# complete the transaction - commit or rollback
secrets_manager.complete_transaction(secret.uid, rollback=not success)

```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="150">Parameter</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td><code>record</code></td><td><code>KeeperRecord</code></td><td>Yes</td><td>Storage and query configuration</td></tr></tbody></table>

Set field values using the [field](#retrieve-standard-fields) method. 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. Some fields have multiple values, in these cases the value can be set to a list.

#### Update a Standard Field Value

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

```python
secret.field(field_type, value=None, single=False)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by record UID
secret = secrets_manager.get_secrets(['<RECORD UID>'])[0]

# update login
secret.field("login", value="My New Login")

# save secret
secrets_manager.save(secret)
```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="168">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>field_type</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Field type to get</td></tr><tr><td><code>value</code></td><td><code>String</code> or <code>String[]</code></td><td>Optional</td><td>None</td><td>If passed, set the value of the field to the given value</td></tr><tr><td><code>single</code></td><td><code>boolean</code></td><td>Optional</td><td>False</td><td>Return only the first value</td></tr></tbody></table>

#### Update a Custom Field Value

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

```python
secret.custom_field(label, field_type=None, value=None, single=False)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by UID
secret = secrets_manager.get_secrets(['EG6KdJaaLG7esRZbMnfbFA'])[0]

# Get a standard template field
password = secret.field('password', single=True)

# Set custom field 'API Key'
my_new_api_key = "wKridl2ULt20qGuiP3IY"
secret.custom_field('API Key', value=my_new_api_key)

# Save changes to the secret
secrets_manager.save(secret)
```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="171">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>label</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Label of the custom field</td></tr><tr><td><code>field_type</code></td><td><code>String</code></td><td>Optional</td><td>None</td><td>Field type to get</td></tr><tr><td><code>value</code></td><td><code>String</code> or <code>String[]</code></td><td>Optional</td><td>None</td><td>If passed, set the value of the field to the given value</td></tr><tr><td><code>single</code></td><td><code>boolean</code></td><td>Optional</td><td>False</td><td>Return only the first value</td></tr></tbody></table>

#### Generate a Random Password

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

```python
generate_password(length, lowercase, uppercase, digits, specialCharacters)
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage
from keeper_secrets_manager_core.utils import generate_password

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# get a specific secret by UID
secret = secrets_manager.get_secrets(['EG6KdJaaLG7esRZbMnfbFA'])[0]

# generate a random password
password = generate_password()
# update a record with new password
secret.field('password', value=password)

# Save changes to the secret
secrets_manager.save(secret)
```

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

### Download a File

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

```python
file.save_file(file_path, create_folders=False)
```

{% endtab %}

{% tab title="Example" %}

```python
# Save all files to a /tmp folder (create folder if does not exist)
for file in secret.files:
    print("file: %s" % file)
    file.save_file("/tmp/" + file.name, True)
```

{% endtab %}
{% endtabs %}

<table data-header-hidden><thead><tr><th width="203">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>file_path</code></td><td><code>String</code></td><td>Yes</td><td></td><td>Path to save file to</td></tr><tr><td><code>create_folders</code></td><td><code>boolean</code></td><td>No</td><td>False</td><td>Create folders in the file_path if not present</td></tr></tbody></table>

### Upload a File

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

```python
upload_file(owner_record, file: my_file)
```

Creating the Keeper File Upload Object:

```python
KeeperFileUpload.from_file(path, file_name=None, file_title=None, mime_type=None)
```

{% endtab %}

{% tab title="Example" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage
from keeper_secrets_manager_core.core import KeeperFileUpload

secrets_manager = SecretsManager(
    config=FileKeyValueStorage('ksm-config.json')
)

# Get an individual secret by UID to attach the file to
UID_FILTER = 'XXX'
owner_record= secrets_manager.get_secrets([UID_FILTER])[0]

# Prepare file data for upload
my_file = KeeperFileUpload.from_file("./myFile.json", "myfile.json", "My File") 

# Upload file attached to the owner record and get the file UID
file_uid = secrets_manager.upload_file(owner_record, file = my_file)
```

{% endtab %}
{% endtabs %}

Upload File

<table><thead><tr><th width="184.14763231197773">Parameter</th><th width="173">Type</th><th width="150">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>owner_record</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>

Keeper File Upload From File

<table><thead><tr><th width="169">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><code>path</code></td><td>string</td><td>Yes</td><td></td><td>Path to the file to upload</td></tr><tr><td><code>file_name</code></td><td>string</td><td>No</td><td>None</td><td>What the name of the file will be in Keeper once uploaded</td></tr><tr><td><code>file_title</code></td><td>string</td><td>No</td><td>None</td><td>What the title of the file will be in Keeper once uploaded</td></tr><tr><td><code>mime_type</code></td><td>string</td><td>No</td><td>None</td><td>The type of data in the file. If none is provided, 'application/octet-stream' will be used</td></tr></tbody></table>

#### Returns

Type: `string`

The file UID of the attached file

### Remove File Attachments

{% tabs %}
{% tab title="Remove Files" %}

```python
secrets_manager.save(record, links_to_remove=['<FILE_UID>'])
```

{% endtab %}

{% tab title="Example Usage" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

secrets_manager = SecretsManager(
    config=FileKeyValueStorage('ksm-config.json')
)

# Get a secret by UID
secret = secrets_manager.get_secrets(['<RECORD_UID>'])[0]

# Get file UIDs from the record
file_uids = [file.file_uid for file in secret.files]
print(f"Files to remove: {file_uids}")

# Remove specific files by UID
secrets_manager.save(secret, links_to_remove=file_uids)
```

{% endtab %}

{% tab title="Remove Multiple Files" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

secrets_manager = SecretsManager(
    config=FileKeyValueStorage('ksm-config.json')
)

# Get a secret by UID
secret = secrets_manager.get_secrets(['<RECORD_UID>'])[0]

# Remove multiple specific files
files_to_remove = ['<FILE_UID_1>', '<FILE_UID_2>']
secrets_manager.save(secret, links_to_remove=files_to_remove)

# Or remove all files
all_file_uids = [file.file_uid for file in secret.files]
secrets_manager.save(secret, links_to_remove=all_file_uids)
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="200">Parameter</th><th width="150">Type</th><th width="150">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>record</code></td><td><code>KeeperRecord</code></td><td>Yes</td><td>The record to update</td></tr><tr><td><code>links_to_remove</code></td><td><code>String</code> or <code>List[String]</code></td><td>No</td><td>File UID(s) to remove from the record</td></tr></tbody></table>

{% hint style="warning" %}
After removing files, make sure to reload the record with `get_secrets()` if you plan to make additional updates, as the record revision will have changed.
{% endhint %}

### 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 [upload\_file](#upload-a-file)

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

```python
from keeper_secrets_manager_core.dto.dtos import RecordCreate,RecordField

record = RecordCreate(record_type='login', title='test_KSM')
record.fields = [ 
    RecordField(field_type='login',value='test_login'),
    RecordField(field_type='password',value='test_pwd') 
]

secrets_manager.create_secret(folder_uid, record)
```

<table><thead><tr><th width="162">Parameter</th><th width="255">Type</th><th>Required</th><th>Default</th></tr></thead><tbody><tr><td><code>folder_uid</code></td><td><code>String</code></td><td>Yes</td><td></td></tr><tr><td><code>record</code></td><td><code>KeeperRecord</code></td><td>Yes</td><td></td></tr></tbody></table>
{% endtab %}

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

```python
from keeper_secrets_manager_core.dto.dtos import RecordCreate,RecordField

record = RecordCreate(record_type='login', title='test_KSM')
record.fields = [ 
    RecordField(field_type='login',value='test_login'),
    RecordField(field_type='password',value='test_pwd') 
]

from keeper_secrets_manager_core.dto.payload import CreateOptions

create_options = CreateOptions(
    folder_uid='shared_folder_uid',
    subfolder_uid='user_folder_uid'
)

secrets_manager.create_secret_with_options(create_options, record)
```

<table><thead><tr><th width="195">Parameter</th><th width="168">Type</th><th width="127">Required</th><th>Default</th></tr></thead><tbody><tr><td><code>create_options</code></td><td><code>CreateOptions</code></td><td>Yes</td><td></td></tr><tr><td><code>record</code></td><td><code>KeeperRecord</code></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 %}

```python
# create a new login record
new_login_record = RecordCreate('login', "Sample KSM Record: Python")
# fill in login and password fields
new_login_record.fields = [
    RecordField(field_type='login', value='username@email.com'),
    RecordField(field_type='password', value=generate_password())
]
# fill in notes
new_login_record.notes = 'This is a Python record creation example'
# create the new record and get its UID
record_uid = secrets_manager.create_secret('[FOLDER UID]', new_login_record)
```

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

<pre class="language-python"><code class="lang-python">from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage
from keeper_secrets_manager_core.dto.dtos import RecordCreate, RecordField
from keeper_secrets_manager_core.utils import generate_password

# setup secrets manger
secrets_manager = SecretsManager(
    # token='US:XXXXXX',
    config=FileKeyValueStorage('ksm-config.json')
)

custom_login = RecordCreate(record_type='Custom Login', title='Sample Custom Type KSM Record: Python')
<strong>custom_login.fields = [
</strong>    RecordField(field_type='host', 
                value={'hostName': '127.0.0.1', 'port': '8080'}, 
                label="My Custom Host lbl", 
                required=True),
    RecordField(field_type='login', 
                value='login@email.com', 
                label='My Custom Login lbl', 
                required=True),
    RecordField(field_type='password', 
                value=generate_password(), 
                label='My Custom Password lbl', 
                required=True),
    RecordField(field_type='url', 
                value='http://localhost:8080/login', 
                label='My Login Page', 
                required=True),
    RecordField(field_type='securityQuestion', 
                value={
                    'question': 'What is one plus one (write just a number)', 
                    'answer': '2'
                }, 
                label='My Question 1', 
                required=True),
    RecordField(field_type='phone', 
                value={
                  'region': 'US', 
                  'number': '510-444-3333', 
                  'ext': '2345', 
                  'type': 'Mobile'}, 
                label='My Phone Number'),
    RecordField(field_type='date', 
                value=1641934793000, 
                label='My Date Lbl', 
                required=True),
    RecordField(field_type='name', 
                value={
                  'first': 'John', 
                  'middle': 'Patrick', 
                  'last': 'Smith'}, 
                label="My Custom Name lbl", 
                required=True),
    RecordField(field_type='oneTimeCode', 
                value='otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&#x26;issuer=Example', 
                label='My TOTP', 
                required=True)
]

custom_login.custom = [
    RecordField(field_type='phone', 
                value={'region': 'US', 'number': '510-222-5555', 'ext': '99887', 'type': 'Mobile'}, 
                label='My Custom Phone Lbl 1'),
    RecordField(field_type='phone', 
                value={'region': 'US', 'number': '510-111-3333', 'ext': '45674', 'type': 'Mobile'}, 
                label='My Custom Phone Lbl 2'),
]

custom_login.notes = "\tThis custom type record was created\n\tvia Python SDK copied from https://docs.keeper.io/secrets-manager/secrets-manager/developer-sdk-library/python-sdk"

record_uid = secrets_manager.create_secret('[FOLDER UID]', custom_login)
</code></pre>

{% endtab %}
{% endtabs %}

#### Returns

Type: `string`

The record UID of the new record

### Delete a Secret

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

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

```
secrets_manager.delete_secret(record_uid)
```

| Parameter    | Type   | Required |
| ------------ | ------ | -------- |
| record\_uid  | string | Yes      |
| {% endtab %} |        |          |

{% tab title="Example" %}

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

# setup secrets manger
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json')
)

# delete a specific secret by record UID
secret = secrets_manager.delete_secret('EG6KdJaaLG7esRZbMnfbFA')
```

{% endtab %}
{% endtabs %}

### Caching

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

**Setup and Configure Cache**

In order to setup caching in the Python SDK, include a caching post function when creating a `SecretsManager` object.

The Python SDK includes a default caching function in `KSMCache` class which stores cached queries to a local file thus serving as a disaster recovery function (as long as there's network connectivity it always prefers network over cached data and will use cache only if web vault is inaccessible). You can create your own caching function using `KSMCache` as a starting point - ex. one that prefers local cache over network access and provide own cache management *(ex. refresh cached data once every 5 min)*

```python
secrets_manager = SecretsManager(
    token='<One Time Access Token>',
    config=FileKeyValueStorage('ksm-config.json'),
    custom_post_function=KSMCache.caching_post_function
)
```

{% hint style="danger" %}
The default caching function in `KSMCache` class always stores last request only - ex. filtered request on UID1 but on disconnect request UID2 from same cache will return empty response *(although UID2 may be shared to the same KSM app but it was not cached)*
{% 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` function to refresh cache *(and pull updated metadata from vault like the new record revision etc.)*
{% endhint %}

#### Creating your own caching function

For start create caching function with following arguments and call `post_function`

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.core import KSMCache, KSMHttpResponse

def caching_post_function(
        url, transmission_key, encrypted_payload_and_signature, verify_ssl_certs=True, proxy_url=None
):
    ksm_rs = SecretsManager.post_function(
        url, transmission_key, encrypted_payload_and_signature, verify_ssl_certs
    )
    # Your custom caching logic here ...

    # Make sure to always return a KSMHttpResponse object
    return ksm_rs
```

Then you can implement any custom logic of caching using KSMCache.\
Full basic example:

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.core import KSMCache, KSMHttpResponse
from keeper_secrets_manager_core.storage import FileKeyValueStorage
from http import HTTPStatus

def caching_post_function(
        url, transmission_key, encrypted_payload_and_signature, verify_ssl_certs=True, proxy_url=None
):
    ksm_rs = SecretsManager.post_function(
        url, transmission_key, encrypted_payload_and_signature, verify_ssl_certs
    )

    if ksm_rs.status_code < 400:
        KSMCache.save_cache(transmission_key.key + ksm_rs.data)
        return ksm_rs

    # KSMCache can be empty    
    cached_data = KSMCache.get_cached_data()
    cached_transmission_key = cached_data[:32]
    transmission_key.key = cached_transmission_key
    data = cached_data[32 : len(cached_data)]

    new_rs = KSMHttpResponse(HTTPStatus.OK, data, None)
    return new_rs


secrets_manager = SecretsManager(
    config=FileKeyValueStorage('ksm-config.json'),
    verify_ssl_certs=False,
    custom_post_function=caching_post_function
)
```

### Folders

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

### Read Folders

Downloads full folder hierarchy.

```python
get_folders()
```

**Response**

Type: `List[KeeperFolder]`

**Example Usage**

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

secrets_manager = SecretsManager(config=FileKeyValueStorage('ksm-config.json'))
folders = secrets_manager.get_folders()
```

### 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.

```python
create_folder(create_options: CreateOptions, folder_name: str, folders=None)
```

<table><thead><tr><th width="200.85161529417937">Parameter</th><th width="225.99431983802478">Type</th><th width="102">Required</th><th width="86">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>create_options</code></td><td><code>CreateOptions</code></td><td>Yes</td><td></td><td>The parent and sub-folder UIDs</td></tr><tr><td><code>folder_name</code></td><td><code>str</code></td><td>Yes</td><td></td><td>The Folder name</td></tr><tr><td><code>folders</code></td><td><code>List[KeeperFolder]</code></td><td>No</td><td><code>None</code></td><td>List of folders to use in the search for parent and sub-folder from CreateOptions</td></tr></tbody></table>

**Example Usage**

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.core import CreateOptions
from keeper_secrets_manager_core.storage import FileKeyValueStorage

secrets_manager = SecretsManager(config=FileKeyValueStorage('ksm-config.json'))
co = CreateOptions(folder_uid="[PARENT_SHARED_FOLDER_UID]", subfolder_uid="")
new_folder_uid = secrets_manager.create_folder(co, "new_folder")
```

### Update a Folder

Updates the folder metadata - currently folder name only.

```python
update_folder(folder_uid: str, folder_name: str, folders=None)
```

<table><thead><tr><th width="194.85161529417937">Parameter</th><th width="218.99431983802478">Type</th><th width="104">Required</th><th width="92">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>folder_uid</code></td><td><code>str</code></td><td>Yes</td><td></td><td>The folder UID</td></tr><tr><td><code>folder_name</code></td><td><code>str</code></td><td>Yes</td><td></td><td>The new folder name</td></tr><tr><td><code>folders</code></td><td><code>List[KeeperFolder]</code></td><td>No</td><td><code>None</code></td><td>List of folders to use in the search for parent folder</td></tr></tbody></table>

**Example Usage**

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

secrets_manager = SecretsManager(config=FileKeyValueStorage('ksm-config.json'))
secrets_manager.update_folder("[FOLDER_UID]", "new_folder_name")
```

### Delete Folders

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

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

```python
delete_folder(folder_uids: List[str], force_deletion: bool = False)
```

<table><thead><tr><th width="209.85161529417937">Parameter</th><th width="165.99431983802478">Type</th><th width="108">Required</th><th width="101">Default</th><th>Description</th></tr></thead><tbody><tr><td><code>folder_uids</code></td><td><code>List[str]</code></td><td>Yes</td><td></td><td>The folder UID list</td></tr><tr><td><code>force_deletion</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**

```python
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage

secrets_manager = SecretsManager(config=FileKeyValueStorage('ksm-config.json'))
secrets_manager.delete_folder(["[FOLDER_UID1]", "[FOLDER_UID2]"], True)
```

## Proxy support

### Environment variable

Keeper Secrets Manager SDK uses the [requests](https://pypi.org/project/requests/) library to support the HTTPS\_PROXY environment variable by [default](https://requests.readthedocs.io/en/latest/user/advanced/#proxies)

```
HTTPS_PROXY=https://yourproxy:8888 python main.py
```

Every request including Keeper Secrets Manager requests will go through declared proxy

{% hint style="info" %}
Using environment variables for proxy settings is preferred because it keeps configuration out of code, ensures consistency across tools, and simplifies deployment.
{% endhint %}

### SecretsManager parameter

Optionally, you can pass your proxy url to `SecretsManager` if you want proxy to be used only in SDK:

```python
secrets_manager = SecretsManager(
    config=FileKeyValueStorage('ksm-config.json'),
    proxy_url="https://yourproxy:8888"
)
```

{% hint style="info" %}
If your proxy has authentication, just pass your username and password in proxy url
{% endhint %}

```
https://username:pass@yourproxy:8888
```
