---
title: Prelude CLI/SDK setup/usage
description: "The user must configure their CLI profile if they are a new CLI user by running: to login and save their access tokens for 90 days.   After 90 days when the refresh token expires, the user must run Cr"
---

[Skip to content](https://support.preludesecurity.com/prelude-cli/sdk-setup/usage#main-content)

English

Show submenu for translations

[Contact support](https://support.preludesecurity.com/kb-tickets/new?hsLang=en)

![Prelude\_Wordmark\_Black\_Transparent.png\]](https://support.preludesecurity.com/hs-fs/hubfs/Prelude_Wordmark_Black_Transparent.png?height=50&name=Prelude_Wordmark_Black_Transparent.png)

Open main navigation

Close main navigation

- English
  
  Show submenu for translations
- [Contact support](https://support.preludesecurity.com/kb-tickets/new)
- [Visit the website](https://www.preludesecurity.com/)

[Visit the website](https://www.preludesecurity.com/)

 Find what you're looking for

- There are no suggestions because the search field is empty.

1. [Knowledge Base](https://support.preludesecurity.com/?hsLang=en)
2. [CLI](https://support.preludesecurity.com/cli?hsLang=en)

# Prelude CLI/SDK setup/usage

### Installing the CLI

```
pip install prelude-cli
```

### Upgrading the CLI

```
pip install prelude-cli --upgrade
```

### Setting Up the CLI

The user must configure their CLI profile if they are a new CLI user by running:

```
prelude configure
```

### CLI Usage

- Before using any CLI command, the user must run

```
prelude auth login --password <PASSWORD>
```

to login and save their access tokens for 90 days.   After 90 days when the refresh token expires, the user must run

```
prelude auth login --password <PASSWORD>
```

- If for some reason the auto-token refreshing is not working the user can manually run

```
prelude auth refresh
```

- Tokens are saved per user/api (us1/eu1/us2) pair so adding a new account to your keychain with the same user as one of the other profiles in the keychain that is already logged in will not require another authentication call
- Token Expiration Details:  By default, we store an access token and a refresh token. The access token is good for 3 hours but the refresh token is good for 90 days. so whenever the access token returns 401 (unauthenticated) the CLI automatically uses the refresh token (if still valid) to get a new access token and reauthenticate.

### New CLI commands

#### Login using password

- Saves tokens to disk and returns to user

```
prelude auth login --password <PASSWORD>
```

#### Refresh tokens

- Refreshes saved tokens, must have called auth login at least once before

```
prelude auth refresh
```

#### Invite user

 Invite a new user to your account

```
prelude iam invite-user --permission <PERMISSION> --name <INVITED USER NAME> --oidc <OIDC APP NAME or 'none'> <INVITED USER EMAIL>
```

#### Create service user

Create a service user is used to generate a user and registration string for deploying probes and creating endpoints via the CLI

```
prelude iam create-service-user <SERVICE USER NAME>
```

### SDK

#### SDK Snippets

There are various functions to aid in authenticating via the SDK.  Using a password, using an existing token, pulling an existing token automatically etc.

Here are a few snippets, followed by a fuller example that puts them into practice!

```
def init_keychain():    keychain = Keychain()    keychain.configure_keychain(ACCOUNT, HANDLE, HQ, profile="default")    profile_items = dict(keychain.get_profile(profile="default").items())# keychain must have already been configureddef from_keychain_password():    account = Account.from_keychain(profile="default")    controller = IAMAccountController(account)    account.password_login(PASSWORD)    account_details = controller.get_account()# keychain must have already been configured, can only be used if account.password_login(PASSWORD) has been called beforedef from_keychain_refresh():    account = Account.from_keychain(profile="default")    controller = IAMAccountController(account)    account.refresh_tokens()    account_details = controller.get_account()# won't be able to use account.password_login(PASSWORD) or account.refresh_token(), which is restricted to only be used by keychain-instantiated accountsdef from_id_token():    tokens = exchange_token(ACCOUNT, HANDLE, HQ, "password", {"password": PASSWORD})    account = Account.from_token(        ACCOUNT, HANDLE, hq=HQ, token=tokens["token"]    )    controller = IAMAccountController(account)    account_details = controller.get_account()# won't be able to use account.password_login(PASSWORD) or account.refresh_token(), which is restricted to only be used by keychain-instantiated accountsdef from_refresh_token():    tokens = exchange_token(ACCOUNT, HANDLE, HQ, "password", {"password": PASSWORD})    account = Account.from_token(        ACCOUNT, HANDLE, hq=HQ, refresh_token=tokens["refresh_token"]    )    controller = IAMAccountController(account)    account_details = controller.get_account()
```

 

#### SDK Example

In this example, we provide a complete Python script to authenticate and retrieve the SCM statistics/summary.  If already authenticated, it will use an existing token.  If there is no existing token it will use a password that can be provided on the command line, as an environment variable or it will be requested when needed.

 

```
import argparseimport osimport jsonfrom dotenv import load_dotenvimport getpassfrom prelude_sdk.controllers.scm_controller import ScmControllerfrom prelude_sdk.controllers.iam_controller import IAMAccountControllerfrom prelude_sdk.models.codes import (    Control,    ControlCategory,    PartnerEvents,    RunCode,    SCMCategory,)from prelude_sdk.models.account import Account, Keychainaccount = Nonescm = None# Load environment variables from a .env file if it existsload_dotenv()def parse_arguments():    """    Parse command-line arguments for Account initialization.    Returns:        argparse.Namespace: Parsed arguments containing profile, password, and refresh token.    """    parser = argparse.ArgumentParser(description="Retrieve SCM endpoints using Prelude SDK")        parser.add_argument("--profile", default="default", type=str, help="Prelude account profile")    parser.add_argument("--password", required=False, type=str, help="Prelude account password")        return parser.parse_args()def from_keychain_password(profile="default", password=None) -> Account:    """    Authenticate an account using the password stored in the keychain.    Args:        profile (str): Profile name for the account in the keychain.        password (str, optional): Account password. If None, prompts for password input.    Returns:        Account: Authenticated account instance.    """    account = Account.from_keychain(profile=profile)    try:        if password is not None:            account.password_login(password)        else:            password = getpass.getpass("Enter your password: ")            account.password_login(password)    except:        print("Authentication failed")        return None    return accountdef from_keychain_refresh(profile="default") -> Account:    """    Authenticate an account using refresh tokens stored in the keychain.    Args:        profile (str): Profile name for the account in the keychain.    Returns:        Account or None: Authenticated account instance if successful, None otherwise.    """    account = Account.from_keychain(profile=profile)    try:        account.refresh_tokens()    except json.JSONDecodeError:        print("Unable to decode tokens. Login with password.")        return None    except AttributeError:        print("Unable to decode tokens. Login with password.")        return None    except Exception as e1:        print(f"{e1}. Login with password.")        return None    return accountdef setup_account(args):    """    Setup account using provided arguments or environment variables.    Args:        args (argparse.Namespace): Parsed command-line arguments.    Returns:        Account: Authenticated account instance.    """    profile = args.profile or os.getenv("PRELUDE_PROFILE")    password = args.password or os.getenv("PRELUDE_PASSWORD")    account = from_keychain_refresh(profile=profile)    if account is None:        account = from_keychain_password(profile=profile, password=password)            return accountdef get_scm_basic_stats(controller):    """    Retrieve basic SCM evaluation summary from the SCM controller.    Args:        controller (ScmController): Initialized ScmController instance.    Returns:        dict: SCM evaluation summary or error status.    """    try:        summary = controller.evaluation_summary()        return summary    except Exception as e:        print("Error retrieving summary:", str(e))    return {"status": "error", "results": []}def main():    """    Main function to parse arguments, authenticate the account, and retrieve SCM stats.    """    args = parse_arguments()    account = setup_account(args)    if account is None:        exit(1)    # Create an instance of ScmController    scm = ScmController(account)    try:        input("Press enter to get Basic SCM stats: ")        stats = get_scm_basic_stats(scm)        print(json.dumps(stats, indent=4))    except Exception as e:        print("Error retrieving results:", str(e))if __name__ == "__main__":    main()
```

 

Example output:

`% python scm_sdk_new_auth.py`

```
No refresh token found, please login first to continue. Login with password.Enter your password: Press enter to get Basic SCM stats: {    "endpoint_summary": {        "categories": [            {                "category": 5,                "endpoint_count": 302,                "excepted": {                    "control_failure_count": 50,                    "missing_asset_manager_count": 292,                    "missing_edr_count": null                },                "instances": [...]            },            {                "category": 6,                "endpoint_count": 867,                "excepted": {                    "control_failure_count": null,                    "missing_asset_manager_count": null,                    "missing_edr_count": 857                },                "instances": [...]            }        ],        "endpoint_count": 1159    },    "inbox_summary": {        "categories": [            {                "category": 2,                "inbox_count": 9,                "instances": [                    {                        "control": 10,                        "inbox_count": 9,                        "setting_count": 250,                        "excepted": {                            "setting_misconfiguration_count": 116                        }                    }                ]            }        ],        "inbox_count": 9    },    "user_summary": {        "categories": [            {                "category": 3,                "user_count": 6,                "excepted": {                    "control_failure_count": 0                },                "instances": [...]            }        ],        "user_count": 6    }}
```

 

 

- [Monitor](https://support.preludesecurity.com/monitor?hsLang=en#main-content)

    - [Introduction to Control Monitor](https://support.preludesecurity.com/monitor?hsLang=en#introduction-to-control-monitor)
    - [Configuration](https://support.preludesecurity.com/monitor?hsLang=en#configuration)
    - [Client Hardware Security Integrations](https://support.preludesecurity.com/monitor?hsLang=en#client-hardware-security-integrations)
    - [Device Discovery Integrations](https://support.preludesecurity.com/monitor?hsLang=en#device-discovery-integrations)
    - [EDR Integrations](https://support.preludesecurity.com/monitor?hsLang=en#edr-integrations)
    - [Email Integrations](https://support.preludesecurity.com/monitor?hsLang=en#email-integrations)
    - [Identity Management Integrations](https://support.preludesecurity.com/monitor?hsLang=en#identity-management-integrations)
    - [Endpoint Management Integrations](https://support.preludesecurity.com/monitor?hsLang=en#endpoint-management-integrations)
    - [Vulnerability Management Integrations](https://support.preludesecurity.com/monitor?hsLang=en#vulnerability-management-integrations)
    - [Reporting](https://support.preludesecurity.com/monitor?hsLang=en#reporting)
    - [Troubleshooting](https://support.preludesecurity.com/monitor?hsLang=en#troubleshooting)
- [Detect](https://support.preludesecurity.com/detect?hsLang=en#main-content)

    - [Basics](https://support.preludesecurity.com/detect?hsLang=en#basics)
    - [Integrations](https://support.preludesecurity.com/detect?hsLang=en#integrations)
    - [Probe deployment](https://support.preludesecurity.com/detect?hsLang=en#probe-deployment)
    - [Security tests](https://support.preludesecurity.com/detect?hsLang=en#security-tests)
    - [Alert suppression](https://support.preludesecurity.com/detect?hsLang=en#alert-suppression)
    - [Assurance](https://support.preludesecurity.com/detect?hsLang=en#assurance)
- [Integrations](https://support.preludesecurity.com/integrations?hsLang=en#main-content)

    - [SASE](https://support.preludesecurity.com/integrations?hsLang=en#sase)
- [Account](https://support.preludesecurity.com/account?hsLang=en#main-content)

    - [SSO](https://support.preludesecurity.com/account?hsLang=en#sso)
- [CLI](https://support.preludesecurity.com/cli?hsLang=en)
- [Release Notes](https://support.preludesecurity.com/release-notes?hsLang=en)

[![Origin Technology Logo](https://support.preludesecurity.com/hs-fs/hubfs/origin_logo_inverse_000000.png?width=5000&height=5000&name=origin_logo_inverse_000000.png "Origin Technology Logo")](http://originhq.com)

Prelude Security Knowledge Base

Copyright © 2026, Origin Technology