Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.alterauth.com/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Every Alter API key carries an explicit list of scopes that gate which SDK methods it can call. A scope is a string like agents:read or grants:admin. The backend evaluates required-vs-granted on every call — there is no “master key” mode.

Scope form

{resource}:{verb}[:{instance}]
PartMeaningExample
resourceWhat the scope grants access toagents, grants, keys, secrets, idp_users, audit_logs, usage
verbWhat operations are allowedread, write, admin (CRUD hierarchy) — or an action verb like tokens:retrieve
instanceOptional: restrict to a single resource IDagents:write:agt_abc123

CRUD verb hierarchy

The three CRUD verbs are ordered. Granting a higher verb implicitly grants every lower verb on the same resource:
VerbWhat it grants
readList + get
writeRead + create + update + delete
adminWrite + key lifecycle (mint / rotate / revoke / deprecate) + cross-principal operations
So agents:write lets a key call vault.agents.list(), vault.agents.create(...), and vault.agents.delete(...). The admin verb is reserved for key lifecycle (vault.agents.mintKey(), vault.agents.rotateKey(), etc.) and cross-principal operations — not for resource deletion.
The Developer Portal labels admin as Manage in the key-creation modal — same scope string under the hood, less alarming UI.

Action verbs (orthogonal)

Some operations don’t fit a CRUD shape — they’re explicit actions. These are NOT covered by CRUD wildcards.
Action verbWhat it grants
tokens:retrieveFetch a provider access token via app.get_token(...) / app.getToken(...)
proxy:executeForward a request through the proxy via app.proxy_request(...) / app.proxyRequest(...)
connect:initiateMint an Alter Connect session
keys:deriveMint a short-lived attenuated key from this one
audit:emitAppend a row to the audit log (no read access — that’s audit_logs:read)
Action verbs are explicit on purpose. A key with grants:admin cannot retrieve a token unless tokens:retrieve is also granted; the two are independent concerns (administering grant metadata vs accessing the credential the grant points at).

Wildcards

Three wildcard forms exist. All bind to the key’s scope catalog version at mint time — adding a new resource to the catalog later does not silently extend an existing wildcard key’s reach.
WildcardMeans
*Every CRUD resource + every action verb known at this key’s version
*:readRead access to every CRUD resource at this key’s version. Does NOT cover action verbs (e.g. tokens:retrieve, proxy:execute) — action verbs are not part of the CRUD grammar and a separate scope must be granted for each one.
agents:*Every CRUD verb (read/write/admin) on the agents resource
* keys are powerful and require explicit org-level opt-in plus an MFA challenge at mint. They also require a CIDR allowlist by default. Use scoped keys instead unless you specifically need cross-resource access (e.g. a backup tool or a compliance scanner).
The cross-resource wildcards *:read / *:write / *:admin apply only to CRUD scopes. Action verbs like tokens:retrieve, proxy:execute, and keys:derive sit outside the CRUD hierarchy and require explicit grants — they are NEVER implied by *:read. Use * (literal universal) if you need every concrete scope at the key’s version.

Instance scoping

A scope can be pinned to a single resource ID:
agents:write:agt_abc123
This grants write on exactly one agent. It satisfies any agents:write requirement only when the call targets that specific instance — for example, vault.agents.update("agt_abc123", ...) succeeds, but vault.agents.update("agt_xyz", ...) raises InsufficientScopeError. A resource-wide grant covers instance-scoped calls. If the key holds agents:write, a call targeting agt_abc123 succeeds because the wide grant covers the narrow one. The reverse is not true.

Per-request attenuation

A caller can narrow a key’s effective scope set for a single call via the SDK’s with_constraints / withConstraints helper:
narrowed = app.with_constraints(scopes=["grants:read"])
await narrowed.list_grants()      # OK
await narrowed.agents.create(...) # raises InsufficientScopeError
The constraints are transmitted in a tamper-evident way — an upstream proxy cannot strip or rewrite them — and the backend intersects the constraint set with the key’s stored scopes. The constraint can only narrow, never broaden. A broadening attempt raises a typed validation error from the SDK. Use this pattern when:
  • A worker only needs a single capability for the next batch of work — narrow at the call boundary instead of using the full app key.
  • You’re auditing a key’s behavior and want to verify what it does when constrained.
  • A third-party SDK wraps Alter and wants to advertise the minimum scope it requires.

Effective scope = key ∩ constraints

The backend computes the effective scope set as:
effective = key.scopes ∩ request.constraints   (if constraints sent)
          = key.scopes                          (otherwise)
Then it checks whether the required scope is satisfied by effective. Any required scope that is not satisfied raises InsufficientScopeError from the SDK with required, granted, missing, plus scope_version and current_scope_version for version-drift diagnostics.

Scope versioning

Every key is minted at a specific catalog version. Adding a new scope to the catalog bumps the version. Existing keys keep working at their pinned version — they don’t silently gain access to scopes added later. To pick up a new scope, rotate the key. When the missing scope exists only at the current catalog version (i.e. the key is older than the scope), InsufficientScopeError.scope_version_mismatch is True so callers can recommend a rotation rather than a scope-grant change.

Derived keys

vault.keys.derive(...) mints a short-lived attenuated key with a strict narrowing of the parent. The derived key:
  • Has a subset of the parent’s scopes (permission boundary enforced at mint).
  • Inherits the parent’s catalog version (no drift).
  • Has its own CIDR allowlist (must be a subset of the parent’s).
  • Cannot itself derive — keys:derive is stripped at mint time.
  • Has a TTL ceiling — default 24h, capped by org policy max_derived_key_ttl_hours.
Revoking the parent cascades to every derived descendant in the same transaction. Rotation successors (rk/ak) are NOT cascade-revoked — only dk children are.

SDK method scope requirements

Use this matrix to compute the minimum scope set for a given key — a key only needs the union of scopes for the methods it actually calls.
The SDK exposes the entire surface as typed methods (e.g. vault.keys.derive()). You will not normally hit any underlying transport directly. The matrix is for picking a minimum scope set at mint time.
A call whose key does not satisfy the required scope raises InsufficientScopeError with required, granted, and missing populated on the exception.

Grants

SDK methodRequired scope
app.list_grants() / app.listGrants()grants:read
app.revoke_grant(grant_id=...) / app.revokeGrant({grantId})grants:admin (instance-scoped on the grant)
app.create_managed_secret_grant(...) / app.createManagedSecretGrant(...)grants:write

Agents

SDK methodRequired scope
vault.agents.create(...)agents:write
vault.agents.list(...)agents:read
vault.agents.get(agent_id)agents:read (instance-scoped)
vault.agents.update(agent_id, ...)agents:write (instance-scoped)
vault.agents.delete(agent_id)agents:write (instance-scoped)
vault.agents.list_keys(agent_id) / vault.agents.listKeys(agentId)keys:read
vault.agents.mint_key(agent_id, ...) / vault.agents.mintKey(agentId, ...)keys:admin
vault.agents.revoke_key(agent_id, key_id) / vault.agents.revokeKey(...)keys:admin
vault.agents.deprecate_key(agent_id, key_id) / vault.agents.deprecateKey(...)keys:admin
vault.agents.undeprecate_key(agent_id, key_id) / vault.agents.undeprecateKey(...)keys:admin
agent.me() (agent-key only — identifies the calling agent)(no scope — agent-key only)

Keys (scoped-key lifecycle)

SDK methodRequired scope
vault.keys.derive(...)keys:derive
vault.keys.rotate(key_id=...)keys:admin (instance-scoped)
vault.keys.revoke(key_id=...)keys:admin (instance-scoped)

Tokens

SDK methodRequired scope
app.get_token(...) / app.getToken(...)tokens:retrieve
app.get_token(grant_id=...) / app.getToken({grantId})tokens:retrieve (instance-scoped on the grant)

Proxied calls

SDK methodRequired scope
app.proxy_request(...) / app.proxyRequest(...)proxy:execute

OAuth Connect

SDK methodRequired scope
app.create_connect_session(...) / app.createConnectSession(...)connect:initiate AND grants:write
Connect needs both scopes because the session starts a connect flow (connect:initiate) and commits a grant when the user finishes (grants:write). Issuing the session without grants:write would leave the user stuck at the consent screen.

End-user auth (IDP token verification)

SDK methodRequired scope
app.start_user_session(...) / app.startUserSession(...)idp_users:write
app.poll_user_session(...) / app.pollUserSession(...)idp_users:read
app.verify_user_token(...) / app.verifyUserToken(...)idp_users:read

Approvals (HITL)

SDK methodRequired scope
app.get_approval(approval_id=...) / app.getApproval({approvalId})approvals:read
app.get_approval_result(approval_id=...) / app.getApprovalResult({approvalId})approvals:read

Audit log

SDK methodRequired scope
app.emit_audit_event(...) / app.emitAuditEvent(...)audit:emit
audit:emit is an action verb — it is not covered by audit_logs:* wildcards. Reading the audit log is a separate scope (audit_logs:read); emitting and reading are independent permissions so a compromised key with audit:emit cannot exfiltrate audit history.

Scope catalog discovery

SDK methodRequired scope
vault.scopes.list()(no scope — static catalog metadata)
The catalog is static metadata at the current catalog version. The method still requires a valid SDK client (so the env / CIDR / rate-limit gates still apply), but no scope is required to read it.

Worked examples

A read-only analytics worker that lists grants and reads audit logs:
grants:read
audit_logs:read
A connect-session minter in a webhook handler:
connect:initiate
grants:write
A token-retrieval-only backend for a single OAuth grant:
tokens:retrieve:grnt_abc123
(instance-scoped to the one grant the backend should reach) An incident-response key for emergency key rotation:
keys:admin
(plus a CIDR allowlist — most orgs enforce this for keys:admin)

See also

  • Audit logs — every scope decision (allow + deny) is recorded.