Reference
alter agents
Manage managed-agent identities and their API keys.
A managed agent is a first-class identity for an AI workload, with its own keys and its own audit attribution. These commands manage agents and their key lifecycle.
alter agents list [--status ...] [--type ...] [--provider ...] [--search ...]alter agents create --name <name> --scopes <json> [--display-name ...] [--type ...] [--input ...]alter agents show --agent <agent-id>alter agents audit --agent <agent-id> [--limit ...] [--offset ...]alter agents registry-list [--limit ...] [--offset ...]alter agents registry-show --agent <actor-id>alter agents update --agent <agent-id> [--display-name ...] [--metadata ...] [--scopes <json> --expected-version <n>] [--grace-seconds <n>]alter agents suspend --agent <agent-id> [--yes]alter agents resume --agent <agent-id> [--yes]alter agents revoke --agent <agent-id> [--yes]alter agents mint-key --agent <agent-id>alter agents list-keys --agent <agent-id>alter agents revoke-key --agent <agent-id> --key <key-id> [--force] [--yes]alter agents deprecate-key --agent <agent-id> --key <key-id>alter agents undeprecate-key --agent <agent-id> --key <key-id>All commands are app-scoped — pass --app <id> (or the app’s name), or link a workspace / set ALTER_APP_ID. Reads (list / show / list-keys) need dashboard_agents:read; create, metadata-only update, mint-key, deprecate-key, and undeprecate-key need dashboard_agents:write; scope updates, suspend, resume, revoke, and revoke-key need dashboard_agents:admin. audit needs dashboard_audit:read (it reads the audit log, not the agent registry).
| Flag | Default | Description |
|---|---|---|
--status <list> | all | Comma-separated filter: active, inactive, revoked. |
--type <type> | all | agent or service. |
--provider <provider> | all | Agents whose scopes include this provider. |
--search <query> | — | Substring match on name / display name. |
--limit <n> | 10 | Page size (1–10). |
--offset <n> | 0 | Pagination offset. |
alter agents list --status active --type serviceEvery output format emits the agent rows themselves — --output json is the
rows array, matching every other list command. When more agents remain, the
command prints the next --offset to run on stderr, so a | jq pipeline
keeps clean data on stdout.
Paginated audit feed for one agent — the portal actions taken against it (create / update / revoke and key lifecycle), newest first. Needs dashboard_audit:read.
These are management-plane rows, so they follow the same rule as
alter audit: an organization admin sees every
lifecycle event, while a non-admin member sees only the ones they performed and
gets a stderr notice saying so. The agent’s current configuration stays fully
visible to everyone via agents show.
| Flag | Default | Description |
|---|---|---|
--limit <n> | 50 | Page size (1–500). |
--offset <n> | 0 | Pagination offset. |
--output <format> | table | json, jsonl, or table. |
alter agents audit --agent <agent-id> --output tableregistry-list / registry-show
Section titled “registry-list / registry-show”The read-only agent registry — agents seen in audit data, with activity stats (last activity, 30-day count). This is distinct from agents list / show, which manage the configured agent identities; the registry is the observability view. Both need dashboard_agents:read.
registry-list uses --limit 50 by default (range 1–200) and --offset
0. As with list and audit, every format emits the registry rows
themselves (json is the array) and the next --offset prints to stderr
when more remain.
alter agents registry-listalter agents registry-show --agent <actor-id>create
Section titled “create”Create a managed agent. --name and a --scopes object are required unless you supply --input; input bodies must contain the same scope map. An empty object (--scopes '{}') provisions a deny-all agent that cannot reach any provider until scopes are added.
The response carries the agent’s first API key in the api_key field, shown once (mint-key returns later keys the same way). Capture it in the same pipeline so it never lands in the terminal: --output json | jq -r '.api_key'. Note the difference from app keys, where alter keys mint returns the secret as plain_key.
| Flag | Required | Description |
|---|---|---|
--name <name> | yes | Stable identifier: starts with a lowercase letter or digit, then lowercase letters, digits, -, or _ (maximum 255 characters). |
--display-name <name> | no | Human-readable name. |
--type <type> | no | agent (default) or service. |
--scopes <json> | yes | JSON object mapping provider ids to non-empty scope arrays (maximum 12 KiB compact JSON). {} is allowed and means no provider access. List managed secrets by slug under managed_secret_slugs; entries under the legacy managed_secrets key are refused. |
--input <path> | no | JSON body from a file (@path) or stdin (-); replaces the per-field flags. |
The capability allowlist is a ceiling, not a grant, and it is matched by exact
string comparison. Nothing about it blocks a save: naming a provider before it
is configured is legitimate, so an entry that cannot match is reported rather
than rejected. When one is found, the command still exits 0 and prints a
warning on stderr, leaving stdout a clean JSON object:
alter: scope warning — managed secret 'stripe-prd' does not match any active secret in this app; the allowlist entry stays inert (did you mean 'stripe-prod'?) (save was allowed)For managed secrets, list each secret’s slug from alter managed-secrets list --app <app-id> under managed_secret_slugs, for example
{"managed_secret_slugs":["stripe-production"]}. The slug is stable and unique
within the app. Display names and template IDs are not capability identifiers:
two secrets called “Stripe” can have different permissions, and changing a
display name does not change access.
An agent created before slugs may still hold entries under the legacy
managed_secrets key. Those entries match by display name, so each covers
every active secret with that name. They may be kept or removed in an update,
but new entries are refused with legacy_managed_secret_name_rejected. On
create, any legacy entry is new, so the CLI exits with a usage error before
calling the backend. On update, the CLI first reads the agent and keeps only
the names its stored_legacy_managed_secret_names field lists; if that read
fails (for example, the token lacks read access), the CLI sends the update and
the backend’s 422 decides.
Warnings cover a provider absent from the catalog or not configured on this
app, a scope the provider has not published, a * token (matched literally, so
it permits nothing), a managed-secret slug no active secret carries, any entry
under the legacy managed_secrets key (it matches by display name), a legacy
name that several active secrets share (it permits every one of them), an
allowlist that does not cover the scopes
a configured provider requests (the one warning about too NARROW an allowlist —
a grant carrying those scopes is denied once it takes effect), and the reserved
internal sentinel __deny_all__, which is not authorable (send an empty
allowlist {} to deny everything). A token without dashboard_secrets:read or
dashboard_providers:read cannot run the corresponding check and is told so,
rather than seeing an empty result that reads as “nothing wrong”. A very large
allowlist has its advisories capped, with a trailing count of those not shown.
alter agents create --name nightly-sync --type service \ --display-name "Nightly Sync" \ --scopes '{"google":["openid","email"]}'alter agents show --agent <agent-id>update
Section titled “update”Update labels, metadata, or the full capability allowlist. Scope changes require dashboard_agents:admin and the current version from alter agents show; this prevents a stale full-allowlist replacement from restoring access another operator removed. Broadening applies immediately; narrowing uses --grace-seconds or the 24-hour deployment default.
| Flag | Description |
|---|---|
--display-name <name> | New display name. |
--metadata <json> | Replacement metadata object. |
--scopes <json> | Full provider-to-scope allowlist (maximum 12 KiB compact JSON). {} narrows to no provider access. List managed secrets by slug under managed_secret_slugs; a legacy managed_secrets entry may be kept or removed, never added (see create). |
--expected-version <n> | Positive version from the latest agents show response; required with --scopes. |
--grace-seconds <n> | Narrowing grace 0–604800; requires --scopes. Use 0 for immediate application. |
alter agents update --agent <agent-id> --display-name "Nightly Sync (EU)"alter agents update --agent <agent-id> \ --scopes '{"google":["openid"]}' \ --expected-version <version-from-agents-show> \ --grace-seconds 3600A --scopes update rewrites the whole allowlist, so it reports the same
allowlist warnings create does, on stderr and without blocking the save.
If the agent changes before the update commits, the command exits with a conflict. Run agents show again, reconcile the complete desired allowlist against the fresh record, and retry with its new version.
revoke
Section titled “revoke”Revoke an agent. This cascade-revokes all of its keys. Revocation is recoverable — you can re-create an agent with the same scopes — so the prompt is a simple y/N (skip with --yes).
alter agents revoke --agent <agent-id>suspend / resume
Section titled “suspend / resume”Pause an agent without revoking it, then restore it later.
alter agents suspend --agent <agent-id>alter agents resume --agent <agent-id>suspendflips the agent toinactive. Existing keys and grants stay attached, but every call made with that agent’s key starts failing withagent_inactive— credential calls, grant listing, delegation, Connect session minting, key derivation and catalog reads alike.agent.me()keeps working for self-diagnostics, and telemetry ingestion (span emission and automatic provider-call audit reporting) keeps flowing so a suspension never blinds the audit trail.resumeflips the agent back toactive. Any overdue pending scope narrowing applies on the next auth check or sweeper pass after resume.
Both commands prompt with y/N; pass --yes to skip the prompt.
Key lifecycle
Section titled “Key lifecycle”An agent can hold several keys, which lets you rotate without downtime: mint the new key, deploy it, then revoke the old one.
# Mint a fresh key (plaintext shown once, in the api_key field)alter agents mint-key --agent <agent-id>
# List every key (active / deprecated / revoked) with last-used timestampsalter agents list-keys --agent <agent-id>
# Revoke one keyalter agents revoke-key --agent <agent-id> --key <key-id>revoke-key accepts --force to override the last-active-key guard (leaving the agent with no active key) and --yes to skip the prompt.
Before revoking, you can deprecate a key to signal it should be retired. A deprecated key still authenticates — the backend returns an advisory deprecation header on every authenticated response, and the SDKs warn once per client on the first one, so callers can migrate first. It is fully reversible, so there is no prompt.
# Mark a key deprecated (still works; advisory only)alter agents deprecate-key --agent <agent-id> --key <key-id>
# Undo italter agents undeprecate-key --agent <agent-id> --key <key-id>Recipes
Section titled “Recipes”Rotate an agent’s key without downtime
# 1. Mint a fresh key (printed once) and deploy it to the agent.alter agents mint-key --agent <agent-id># 2. Confirm the agent is calling with the new key — check the LAST USED column.alter agents list-keys --agent <agent-id># 3. Revoke the superseded key.alter agents revoke-key --agent <agent-id> --key <old-key-id>Related
Section titled “Related”alter keys— app-level runtime keysalter managed-secrets grants— give an agent a backend credential- Agents