---
title: Managing Secrets
path: api-guides/secrets
status: published
---

# Managing Secrets

Read, write, version, delete, and list secrets. The day-to-day work of using ScaiVault.

**Base path:** `/v1/secrets/`

For the conceptual model, see [Secrets](../core-concepts/secrets). For endpoint reference, see [Secrets Reference](../reference/secrets).

## Read a secret

```bash
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials
```

```python
import os, httpx

resp = httpx.get(
    "https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials",
    headers={"Authorization": f"Bearer {os.environ['SCAIVAULT_TOKEN']}"},
)
resp.raise_for_status()
secret = resp.json()
print(secret["data"]["client_id"])
```

```typescript
const resp = await fetch(
  "https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials",
  { headers: { "Authorization": `Bearer ${process.env.SCAIVAULT_TOKEN}` } },
);
const secret = await resp.json();
console.log(secret.data.client_id);
```

Read a specific version:

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials?version=2"
```

## Write a secret

Writing creates a new version (or the first version).

```bash
curl -X PUT https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "client_id": "3MVG9...",
      "client_secret": "REDACTED"
    },
    "secret_type": "json",
    "metadata": {
      "description": "Salesforce OAuth credentials",
      "tags": ["salesforce", "oauth", "production"]
    },
    "options": {
      "max_versions": 10,
      "expires_in": "90d",
      "rotation_policy_id": "rot_quarterly"
    }
  }'
```

```python
resp = httpx.put(
    "https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials",
    headers={"Authorization": f"Bearer {os.environ['SCAIVAULT_TOKEN']}"},
    json={
        "data": {"client_id": "3MVG9...", "client_secret": "REDACTED"},
        "secret_type": "json",
        "metadata": {"tags": ["salesforce", "production"]},
        "options": {"max_versions": 10},
    },
)
```

```typescript
const resp = await fetch(
  "https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials",
  {
    method: "PUT",
    headers: {
      "Authorization": `Bearer ${process.env.SCAIVAULT_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      data: { client_id: "3MVG9...", client_secret: "REDACTED" },
      secret_type: "json",
      metadata: { tags: ["salesforce", "production"] },
      options: { max_versions: 10 },
    }),
  },
);
```

Response for a new secret (`201 Created`):

```json
{
  "path": "environments/production/salesforce/api-credentials",
  "secret_type": "json",
  "version": 1,
  "created": true,
  "created_at": "2026-04-23T14:00:00Z",
  "expires_at": "2026-07-22T14:00:00Z"
}
```

For an update (`200 OK`):

```json
{
  "path": "environments/production/salesforce/api-credentials",
  "version": 4,
  "created": false,
  "previous_version": 3,
  "updated_at": "2026-04-23T14:00:00Z"
}
```

## Write fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `data` | object | Yes | The secret payload |
| `secret_type` | string | No | `kv` (default), `json`, `certificate`, `ssh_key`, `api_key` |
| `metadata` | object | No | Non-secret metadata |
| `metadata.description` | string | No | Human-readable description |
| `metadata.tags` | array of string | No | Searchable tags |
| `metadata.owner` | string | No | Owner identity (team, user, group) |
| `options` | object | No | Secret-level settings |
| `options.max_versions` | integer (1–100) | No | Version retention cap |
| `options.expires_in` | duration | No | Relative expiration (`90d`, `24h`) |
| `options.expires_at` | ISO 8601 | No | Absolute expiration |
| `options.rotation_policy_id` | string | No | Attach a rotation policy |
| `options.secret_policy_id` | string | No | Attach a value-generation policy |

## Update metadata without new version

```bash
curl -X PATCH https://scaivault.scailabs.ai/v1/secrets/app/db/password \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "metadata": {
      "tags": ["postgres", "production", "critical"]
    },
    "options": {"max_versions": 25}
  }'
```

Returns updated metadata. Does **not** create a new version or invalidate caches.

## List secrets

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets?prefix=environments/production/&limit=50"
```

```python
resp = httpx.get(
    "https://scaivault.scailabs.ai/v1/secrets",
    headers={"Authorization": f"Bearer {os.environ['SCAIVAULT_TOKEN']}"},
    params={"prefix": "environments/production/", "limit": 50},
)
listing = resp.json()
for s in listing["secrets"]:
    print(s["path"], s["version"])

# Paginate
while listing["has_more"]:
    resp = httpx.get(
        "https://scaivault.scailabs.ai/v1/secrets",
        headers={"Authorization": f"Bearer {os.environ['SCAIVAULT_TOKEN']}"},
        params={"prefix": "environments/production/", "cursor": listing["cursor"]},
    )
    listing = resp.json()
```

```typescript
const params = new URLSearchParams({ prefix: "environments/production/", limit: "50" });
const resp = await fetch(
  `https://scaivault.scailabs.ai/v1/secrets?${params}`,
  { headers: { "Authorization": `Bearer ${process.env.SCAIVAULT_TOKEN}` } },
);
const listing = await resp.json();
for (const s of listing.secrets) console.log(s.path, s.version);
```

Response:

```json
{
  "secrets": [
    {
      "path": "environments/production/salesforce/api-credentials",
      "secret_type": "json",
      "version": 3,
      "updated_at": "2026-04-20T14:22:00Z",
      "expires_at": null,
      "tags": ["salesforce", "oauth"]
    }
  ],
  "cursor": "eyJpZCI6MTc4fQ",
  "has_more": true,
  "total_count": 127
}
```

### List filters

| Parameter | Description |
|-----------|-------------|
| `prefix` | Path prefix |
| `tag` | Tag (repeatable) |
| `secret_type` | `kv`, `json`, etc. |
| `expires_before` | ISO timestamp |
| `updated_after` | ISO timestamp |
| `include_expired` | Include secrets past `expires_at` |
| `include_metadata` | Return full metadata (default: minimal) |
| `limit` | Max per page (default 50, max 200) |
| `cursor` | Next-page cursor |

## List secret versions

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

```json
{
  "path": "app/db/password",
  "versions": [
    {"version": 3, "created_at": "2026-04-20T...", "is_current": true},
    {"version": 2, "created_at": "2026-03-15T...", "is_current": false},
    {"version": 1, "created_at": "2026-01-01T...", "is_current": false}
  ]
}
```

## Get metadata only

Useful when you want version number, tags, rotation status without reading the value — e.g., for inventory or dashboards.

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

Response:

```json
{
  "path": "app/db/password",
  "current_version": 3,
  "version_count": 3,
  "metadata": {
    "description": "PostgreSQL production password",
    "tags": ["postgres", "production"]
  },
  "created_at": "2026-01-01T12:00:00Z",
  "updated_at": "2026-04-20T14:22:00Z",
  "last_accessed_at": "2026-04-23T10:15:00Z",
  "access_count_30d": 1547,
  "rotation_policy": {
    "id": "rot_quarterly",
    "next_rotation": "2026-07-20T00:00:00Z"
  }
}
```

## Delete a secret

Soft delete (recoverable for 30 days by default):

```bash
curl -X DELETE https://scaivault.scailabs.ai/v1/secrets/app/db/password \
  -H "Authorization: Bearer $TOKEN"
```

Hard delete (permanent, requires `admin`):

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

Delete only a specific version:

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

Restore a soft-deleted secret:

```bash
curl -X POST https://scaivault.scailabs.ai/v1/secrets/app/db/password/restore \
  -H "Authorization: Bearer $TOKEN"
```

## Rotate a secret

Triggers immediate rotation. If the secret has a rotation policy attached and `auto_generate: true`, ScaiVault generates the new value. Otherwise, supply `new_value`.

```bash
curl -X POST https://scaivault.scailabs.ai/v1/secrets/app/db/password/rotate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Suspected compromise",
    "new_value": {"password": "new-strong-password"},
    "grace_period": "1h"
  }'
```

Returns the new version. Old version stays readable for the grace period.

## Find expiring secrets

```bash
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets/expiring?days=30"
```

```json
{
  "secrets": [
    {
      "path": "test/oauth-token",
      "expires_at": "2026-05-02T00:00:00Z",
      "days_remaining": 9
    }
  ]
}
```

## Partner-scoped secrets

Write shared values visible to every tenant under the partner:

```bash
curl -X PUT https://scaivault.scailabs.ai/v1/partner/secrets/shared/trust-anchor \
  -H "Authorization: Bearer $PARTNER_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": {"cert_pem": "..."}, "secret_type": "certificate"}'
```

Every tenant reads with `GET /v1/partner/secrets/shared/trust-anchor`. Only partner admins can write.

## Cross-tenant access

Partner admins can act as a specific tenant by prefixing `/v1/t/{tenant_id}/`:

```bash
curl -H "Authorization: Bearer $PARTNER_ADMIN_TOKEN" \
     https://scaivault.scailabs.ai/v1/t/tnt_acme_dev/secrets/app/db/password
```

All audit log entries record both the acting identity and the target tenant.

## Common error codes

| Code | When |
|------|------|
| `secret_not_found` | Path doesn't exist in this tenant |
| `secret_exists` | Soft-deleted path in retention window |
| `version_not_found` | Version aged out or doesn't exist |
| `invalid_path` | Path format violation |
| `version_conflict` | Concurrent write; refetch and retry |
| `access_denied` | No policy permits this action |
| `secret_expired` | `expires_at` has passed |

## What's next

- [Batch Operations](./batch-operations) — reading many secrets at once.
- [Rotation Policies](./rotation) — automated rotation.
- [Policies](./policies) — controlling access.
- [Secrets Reference](../reference/secrets) — full endpoint spec.
