---
name: datatailr-kv-and-secrets
description: Use Datatailr's key-value store and secrets manager. KV stores configuration data and settings. Secrets stores encrypted sensitive values like passwords, API keys, and tokens. Use when the user asks to store or retrieve config, settings, secrets, API keys, passwords, or values on Datatailr / dt.
---

# Key-Value Store & Secrets on Datatailr

Datatailr provides two related stores:

- **KV** -- plain-text key-value pairs for configuration, settings, feature flags, etc.
- **Secrets** -- encrypted key-value pairs for passwords, API keys, tokens, and other sensitive data.

## Creating KV entries and secrets

KV entries and secrets are managed through the **Datatailr UI** (Secrets Manager), not programmatically. When your code needs a KV key or secret that doesn't exist yet, **ask the user to create it in the UI** rather than trying to create it from code or CLI.

For example: "This workflow needs a KV key called `portfolio_holdings`. Please create it in the Datatailr Secrets Manager UI before deploying."

## Python SDK -- KV (read-only)

The SDK is already installed in the workspace -- do not look for it. Jobs read KV values at runtime but should not create or modify them programmatically.

```python
from datatailr import KV

kv = KV()
url = kv.get("db_url")
keys = kv.ls()  # list all keys with their values and ACLs
```

**Important behavior:**

- `KV` is a class -- always instantiate it: `kv = KV()` then `kv.get(...)`. Calling `KV.get(...)` without parentheses will fail.
- `kv.get()` **raises an exception** if the key does not exist. Always wrap in a try/except when the key might be missing:

```python
try:
    value = kv.get("my_key")
except Exception:
    value = "default_value"
```

- `kv.get()` **may return a string or an already-parsed object** (e.g., dict) depending on the platform version and how the value was stored. When reading JSON, always handle both cases:

```python
import json

val = kv.get("config")
if isinstance(val, str):
    val = json.loads(val)
# val is now a dict regardless of what kv.get() returned
```

## Python SDK -- Secrets (read-only)

Jobs retrieve secrets at runtime but never create or modify them in code. If a secret is missing, ask the user to create it in the Datatailr UI.

```python
from datatailr import Secrets

secrets = Secrets()
db_password = secrets.get("database_password")  # returns str
all_secrets = secrets.ls()                       # list available secret keys
```

**Important behavior:**

- `Secrets` is a class -- always instantiate it: `secrets = Secrets()` then `secrets.get(...)`. Calling `Secrets.get(...)` without parentheses will fail.
- `secrets.get()` **raises an exception** if the key does not exist.
- `secrets.get()` always returns a **string**.

## CLI (read-only)

Use CLI commands to check existing KV entries and secrets. Creating, updating, and deleting entries should be done through the Datatailr UI.

### KV commands

```bash
dt kv get my_key                               # retrieve a value
dt kv ls                                       # list all keys
dt kv ls --json                                # JSON output
dt kv ls -v                                    # include values in listing
```

### Secrets commands

```bash
dt kv get -s api_key                           # retrieve a secret
dt kv ls -s                                    # list secret keys
```

### Safety

**Never log, print, or echo secret values.** When the agent retrieves a secret for debugging, it should describe the result (e.g., "the secret exists and is 40 characters long") rather than displaying the value.

## When to use what

| Need | Use |
|---|---|
| Database URLs, API endpoints, feature flags | **KV** |
| Passwords, API keys, tokens, certificates | **Secrets** |
| Large files, binary data, model artifacts | **Blob storage** (see the blob-storage skill) |

## Common patterns

### Reading config in a job

```python
from datatailr import KV, Secrets

kv = KV()
secrets = Secrets()

db_host = kv.get("db_host")
db_password = secrets.get("db_password")
connection_string = f"postgresql://app:{db_password}@{db_host}:5432/mydb"
```
