Secrets API

Org-scoped encrypted secrets (the strongbox service): store provider keys and app credentials once, reference them by name everywhere (e.g. the AI provider proxy reads provider keys from here at request time).

get and rotate return CLEARTEXT

list / create / update return metadata only — never values. But get and rotate return the decrypted cleartext value. Treat any credential that can call them as able to read every secret it can name: scope keys narrowly, audit reads via the audit endpoint, and never log the response.

Endpoints

MethodPathSDK (client.secrets)Returns value?
GET/v1/orgs/{org_id}/secretslistmetadata only
POST/v1/orgs/{org_id}/secretscreatemetadata only
GET/v1/orgs/{org_id}/secrets/{name}getcleartext
PUT/v1/orgs/{org_id}/secrets/{name}updatemetadata only
DELETE/v1/orgs/{org_id}/secrets/{name}delete
POST/v1/orgs/{org_id}/secrets/{name}/rotaterotatecleartext (new value)
GET/v1/orgs/{org_id}/auditauditaccess log

Secret names are validated server-side: 1–256 chars from [A-Za-z0-9._-]; duplicates return 409 DUPLICATE_NAME.

SDK Examples

// Create (response is metadata — the value is write-only here)
await client.secrets.create(orgId, {
  name: 'anthropic-api-key',
  value: process.env.ANTHROPIC_KEY!,
});

// List — names + metadata, no values
const secrets = await client.secrets.list(orgId);

// Read the decrypted value (CLEARTEXT — handle with care)
const { value } = await client.secrets.get(orgId, 'anthropic-api-key');

// Rotate to a fresh random value, returned once
const rotated = await client.secrets.rotate(orgId, 'webhook-signing-key');

// Who read what, when
const audit = await client.secrets.audit(orgId);
client.secrets().create(org_id, &CreateSecretRequest {
    name: "anthropic-api-key".into(),
    value: std::env::var("ANTHROPIC_KEY")?,
}).await?;

let value = client.secrets().get(org_id, "anthropic-api-key").await?; // cleartext
let audit = client.secrets().audit(org_id).await?;