Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

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. For endpoint reference, see Secrets Reference.

Read a secret#

bash
1
2
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/environments/production/salesforce/api-credentials
python
1
2
3
4
5
6
7
8
9
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
1
2
3
4
5
6
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
1
2
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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
1
2
3
4
5
6
7
8
{
  "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
1
2
3
4
5
6
7
{
  "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
1
2
3
4
5
6
7
8
9
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
1
2
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets?prefix=environments/production/&limit=50"
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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
1
2
3
4
5
6
7
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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "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
1
2
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/app/db/password/versions
json
1
2
3
4
5
6
7
8
{
  "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
1
2
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/metadata/app/db/password

Response:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
  "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
1
2
curl -X DELETE https://scaivault.scailabs.ai/v1/secrets/app/db/password \
  -H "Authorization: Bearer $TOKEN"

Hard delete (permanent, requires admin):

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

Delete only a specific version:

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

Restore a soft-deleted secret:

bash
1
2
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
1
2
3
4
5
6
7
8
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
1
2
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets/expiring?days=30"
json
1
2
3
4
5
6
7
8
9
{
  "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
1
2
3
4
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
1
2
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#

Updated 2026-05-17 13:26:50 View source (.md) rev 2