Skip to content

API Keys & Scopes

Keys authenticate every call to the Ovrin API. They are stored hashed — a lost key is replaced, never recovered.

Scopes

A key's scope decides which surface it can touch. A scoped key used anywhere else is rejected with 403, so a leaked partial key cannot read usage, mint keys, or touch billing.

ScopeCan use
allEverything (default)
sandboxes/sandboxes/* only — lifecycle, exec, files, code, endpoints
memory/memory/* only
secrets/secrets/* only
payments/payments/* only — but see the agent key
python
key = client.keys.create(name="ci", scope="sandboxes",
                         expires_at="2026-12-01T00:00:00Z")
ts
const key = await client.keys.create({
  name: "ci",
  scope: "sandboxes",
  expiresAt: "2026-12-01T00:00:00Z",
});

The raw key is returned once. Store it immediately.

Expiry

Pass an RFC 3339 timestamp and the key stops authenticating at that moment — at the control plane and at the tenant lookup the runtime uses, so sandbox creation through any SDK dies with it. Expired keys are indistinguishable from invalid ones by design.

Rotation

Rotation swaps a key in one server transaction: the old hash is revoked as the replacement is minted, so there is no window with two live keys — or none.

python
fresh = client.keys.rotate(key_id)               # same name + scope
fresh = client.keys.rotate(key_id, expires_at=...)  # fresh lifetime
ts
const fresh = await client.keys.rotate(keyId);

The replacement's raw key arrives once, in this response.

Revocation

DELETE /keys/{id} (or client.keys.delete) marks a key revoked; calls fail immediately. The console asks twice before firing.

The agent-payments key

The one payments-scoped key per account is special: a copy is kept encrypted server-side so sandboxes created with payments=true can inject it, letting code inside transact without holding your account key. Mint and rotate it through the payments surface:

python
client.payments.agent_key_status()
raw = client.payments.create_agent_key()   # rotates if one exists

POST /keys/{id}/rotate refuses payments keys (409) on purpose — rotating through the generic endpoint would desync that encrypted copy.