Skip to content

client.secrets

client.secrets — the secrets operations.

On AsyncKindgi every method is the same, awaited.

list(
*,
env_name: str,
scope_kind: Literal['tenant', 'org', 'project'],
scope_id: str | None = None,
inherit: bool | None = None,
limit: int | None = None,
cursor: str | None = None,
name_prefix: str | None = None,
timeout: float | None = None,
) -> SecretCollectionPage

List secret metadata. GET /v1/secrets

Cursor-paginated list of secret metadata at the given (scope, envName). value is ALWAYS absent (structural redaction).

set(
*,
idempotency_key: str | None = None,
timeout: float | None = None,
**fields: Any,
) -> SecretSetResponse

Create a secret or add a version. POST /v1/secrets

Accepts a plaintext value (as do POST /v1/secrets/:name/rotate via newValue and POST /v1/deployments/:deploymentId/secrets); every other secrets route is metadata-only. Requires the secrets:write capability. writeMode: create-new returns 201 with the record; add-version returns 200. Existing-secret conflicts on create-new return 409 secret-write-conflict.

get(
*,
env_name: str,
scope_kind: Literal['tenant', 'org', 'project'],
scope_id: str | None = None,
inherit: bool | None = None,
timeout: float | None = None,
) -> SecretRecord

Get secret metadata. GET /v1/secrets/{name}

revoke(
*,
env_name: str,
scope_kind: Literal['tenant', 'org', 'project'],
scope_id: str | None = None,
hard: bool | None = None,
reason: str | None = None,
timeout: float | None = None,
) -> SecretRevokeResult

Revoke a secret. DELETE /v1/secrets/{name}

Requires the secrets:revoke capability (soft-revoke) or secrets:revoke:hard when ?hard=true (cryptographic erasure). Optional ?reason=<text> records the revoke reason.

list_versions(
*,
env_name: str,
scope_kind: Literal['tenant', 'org', 'project'],
scope_id: str | None = None,
limit: int | None = None,
cursor: str | None = None,
timeout: float | None = None,
) -> SecretVersionCollectionPage

List secret versions. GET /v1/secrets/{name}/versions

Cursor-paginated version metadata for a single secret. value is ALWAYS null on the wire.

get_version(
*,
env_name: str,
scope_kind: Literal['tenant', 'org', 'project'],
scope_id: str | None = None,
timeout: float | None = None,
) -> SecretVersionRecord

Get one secret-version record. GET /v1/secrets/{name}/versions/{versionId}

rotate(
*,
env_name: str | None = None,
scope_kind: Literal['tenant', 'org', 'project'] | None = None,
scope_id: str | None = None,
idempotency_key: str | None = None,
timeout: float | None = None,
**fields: Any,
) -> SecretRotateResponseSync

Rotate a secret (sync or async). POST /v1/secrets/{name}/rotate

Requires the secrets:rotate capability. The scope comes from body scope, else from scopeKind + scopeId in the query; one of them is required, and when both are present they must name the same scope. Authorization checks that scope. Sync providers return 201 with { kind: "sync", newVersionId, oldVersionId, oldVersionRevokedAt? }. Async providers return 202 with { kind: "async", rotationId, statusUrl, eventsUrl }; poll via GET /v1/secrets/:name/rotations/:rotationId or subscribe via SSE at the events URL. kind is the discriminant.

get_rotation_status(
*,
env_name: str,
scope_kind: Literal['tenant', 'org', 'project'],
scope_id: str | None = None,
timeout: float | None = None,
) -> RotationStatus

Get async rotation status. GET /v1/secrets/{name}/rotations/{rotationId}

rotation_events(
*,
env_name: str,
scope_kind: Literal['tenant', 'org', 'project'],
scope_id: str | None = None,
timeout: float | None = None,
) -> None

Subscribe to async rotation events (SSE). GET /v1/secrets/{name}/rotations/{rotationId}/events

text/event-stream — one rotation-update frame per status change; a ping heartbeat every 15s keeps proxies from timing the stream out; the stream closes on terminal state.