---
title: Cookbook
path: api-guides/cookbook
status: published
---

# Cookbook

Short, copy-pasteable recipes for things you'll do often. Each entry has a one-line scenario and the code that does it. For deeper explanations, follow the cross-links.

## Reading

### Read one secret and use a single field

```python
from scaivault_sdk import SyncScaiVaultClient
client = SyncScaiVaultClient(...)
db = client.secrets.read("environments/production/billing/database")
conn_str = db.data["url"]
```

### Read many secrets at startup

```python
batch = client.secrets.batch_read([
    "environments/production/billing/database",
    "environments/production/billing/stripe",
    "environments/production/billing/sendgrid",
])
for path, secret in batch.secrets.items():
    apply_config(path, secret)
```

See [Batch Operations](./batch-operations).

### Read with retry on transient failure

```python
import time
from scaivault_sdk.errors import RateLimitError, ServiceUnavailableError

for attempt in range(5):
    try:
        secret = client.secrets.read(path)
        break
    except RateLimitError as e:
        time.sleep(e.retry_after)
    except ServiceUnavailableError:
        time.sleep(2 ** attempt)
else:
    raise RuntimeError("max retries exceeded")
```

### Read a specific version

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets/app/db/password?version=2"
```

### Check whether a secret exists without reading it

```python
try:
    client.secrets.read_metadata(path)
    exists = True
except NotFoundError:
    exists = False
```

`read_metadata` returns version count, tags, rotation status — but not the value, and only requires `secrets:list` not `secrets:read`.

## Writing

### Idempotent write of initial value

```python
try:
    client.secrets.read_metadata(path)
    # exists; do nothing
except NotFoundError:
    client.secrets.write(path, data={"key": initial_value}, secret_type="api_key")
```

### Update one field without overwriting others

ScaiVault doesn't have field-level update. Read, mutate, write:

```python
secret = client.secrets.read("app/config")
new_data = {**secret.data, "feature_flag_x": True}
client.secrets.write("app/config", data=new_data, secret_type=secret.secret_type)
```

### Write a JSON blob from a file

```bash
scaivault secrets write app/config \
  --type json \
  --json-file ./config.json
```

### Write with expiration

```python
client.secrets.write(
    path="test/oauth-token",
    data={"token": value},
    options={"expires_in": "1h"},
)
```

## Rotation

### Force rotation now

```bash
curl -X POST https://scaivault.scailabs.ai/v1/secrets/$PATH/rotate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason": "compromise"}'
```

### List secrets due for rotation in the next week

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/rotation/due?within_hours=168" \
  | jq -r '.secrets[] | "\(.path) \(.next_rotation_at)"'
```

### Find secrets that missed their rotation window

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/rotation/due?include_overdue=true" \
  | jq -r '.secrets[] | select(.is_overdue) | .path'
```

### Subscribe to rotation events on production secrets

```bash
curl -X POST https://scaivault.scailabs.ai/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod-rotations",
    "url": "https://ops.acme.example/scaivault/rotations",
    "secret": "whsec_...",
    "events": ["rotation.due", "rotation.overdue", "rotation.failed"],
    "filters": {"path_prefix": "environments/production/"}
  }'
```

## Policies and access

### Grant a service account read access to one path

```bash
curl -X POST https://scaivault.scailabs.ai/v1/policies \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "sa-billing-stripe-read",
    "rules": [{"path_pattern": "environments/production/billing/stripe", "permissions": ["read"]}]
  }'

curl -X POST https://scaivault.scailabs.ai/v1/policies/pol_abc/bindings \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"identity_type": "service_account", "identity_id": "sa:billing-prod"}'
```

### Test whether an identity can read a path

```bash
curl -X POST https://scaivault.scailabs.ai/v1/policies/test \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "identity_id": "sa:billing-prod",
    "path": "environments/production/billing/stripe",
    "permission": "read"
  }' | jq '.allowed'
```

### List who has access to a path

ScaiVault doesn't have a direct "who can read X" endpoint, but you can list all policies and filter:

```bash
curl -H "Authorization: Bearer $TOKEN" https://scaivault.scailabs.ai/v1/policies \
  | jq -r '.policies[] | select(.rules[].path_pattern as $p | "environments/production/billing/stripe" | test($p)) | .id'
```

(Glob-pattern matching in jq is rough; for production use the SDK's `policies.list_for_path()`.)

## PKI

### Issue a 7-day mTLS cert

```bash
curl -X POST https://scaivault.scailabs.ai/v1/pki/issue/svc-mtls \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"common_name": "billing.svc.cluster.local", "ttl": "168h"}'
```

### Renew a cert ahead of schedule

```bash
curl -X POST https://scaivault.scailabs.ai/v1/pki/certificates/cert_abc/renew \
  -H "Authorization: Bearer $TOKEN"
```

### Validate a cert chain a client gave you

```bash
curl -X POST https://scaivault.scailabs.ai/v1/pki/certificates/validate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"certificate_pem\": \"$(cat client.crt | sed -z 's/\\n/\\\\n/g')\",
    \"chain_pem\":      \"$(cat client-chain.crt | sed -z 's/\\n/\\\\n/g')\",
    \"check_revocation\": true
  }"
```

### List certs expiring in 30 days

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/pki/certificates?expiring_within=30d" \
  | jq -r '.data[] | "\(.common_name) \(.valid_until)"'
```

### Revoke a cert

```bash
curl -X POST https://scaivault.scailabs.ai/v1/pki/revoke \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"serial_number": "1A:2B:...", "reason": "key_compromise"}'
```

## Dynamic credentials

### One-shot Postgres credential for a script

```python
with client.dynamic.credentials("postgres-prod", "readonly", ttl="30m") as creds:
    run_query(creds["connection_url"])
# Lease revoked automatically on exit
```

### AWS IAM credentials for a deploy job

```bash
lease=$(curl -fsS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ttl":"15m"}' \
  "https://scaivault.scailabs.ai/v1/dynamic/engines/aws-prod/creds/deploy")
export AWS_ACCESS_KEY_ID=$(echo "$lease" | jq -r '.data.access_key_id')
export AWS_SECRET_ACCESS_KEY=$(echo "$lease" | jq -r '.data.secret_access_key')

# Do the deploy
terraform apply -auto-approve

# Revoke
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  "https://scaivault.scailabs.ai/v1/dynamic/leases/$(echo "$lease" | jq -r '.lease_id')"
```

### Emergency revoke all leases from an engine

```bash
curl -X POST https://scaivault.scailabs.ai/v1/dynamic/leases/revoke-prefix \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prefix": "lease_", "engine": "postgres-prod"}'
```

## Audit

### Who read this secret in the last 24 hours?

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/audit/secrets/environments/production/billing/stripe?from=$(date -u -d '1 day ago' +%Y-%m-%dT%H:%M:%SZ)" \
  | jq -r '.logs[] | "\(.timestamp) \(.identity_id) \(.source_ip)"'
```

### Find every action by an ex-employee

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/audit/identities/user:alice@acme.example?from=2026-01-01"
```

Then rotate everything in the response's `summary.resources_accessed`.

### Export last month for compliance

```bash
curl -X POST https://scaivault.scailabs.ai/v1/audit/export \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-04-01T00:00:00Z",
    "to":   "2026-04-30T23:59:59Z",
    "format": "jsonl",
    "destination": {"type": "s3", "bucket": "acme-audit", "prefix": "scaivault/2026-04/", "credentials_path": "infra/aws/audit-uploader"}
  }'
```

## Health and observability

### Smoke test the API

```bash
curl -fsS https://scaivault.scailabs.ai/v1/health
```

### Detailed component health

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/health/detailed | jq
```

### Confirm a token works and see its identity

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/auth/whoami
```

### Find your own rate-limit headroom

```bash
curl -i -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets?limit=1 \
  | grep -i ratelimit
# X-RateLimit-Limit: 1000
# X-RateLimit-Remaining: 982
# X-RateLimit-Reset: 1714478460
```

## Federation

### Read from HashiCorp Vault via ScaiVault

(Assumes federation backend already configured.)

```bash
# Native path: kv/data/apps/billing/db in Vault
# Federation path: external/hashicorp/apps/billing/db
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/external/hashicorp/apps/billing/db
```

### Trigger an immediate sync

```bash
curl -X POST -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/federation/backends/fed_abc/sync
```

## Cleanup and migration

### Soft-delete then restore

```bash
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/old/path
# ... later ...
curl -X POST -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/old/path/restore
```

### Bulk-tag secrets matching a pattern

```python
listing = client.secrets.list(prefix="environments/staging/", limit=200)
for item in listing.data:
    client.secrets.update_metadata(
        path=item.path,
        metadata={"tags": list(set(item.tags + ["needs-rotation"]))},
    )
```

### Find unused secrets (30-day baseline)

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets?include_metadata=true&limit=200" \
  | jq -r '.secrets[] | select(.access_count_30d == 0) | .path'
```

## What's next

- [Managing Secrets](./secrets) — full secret API.
- [Tutorials](../tutorials/) — longer scenarios that use these recipes.
- [Reference](../reference/) — every endpoint.
