Agents API
The agents API is project-scoped: list, inspect health, mint and revoke. There is no token rotation endpoint and no rotate-in-place operation. Replacing a credential means revoking the old agent and minting a new row, ID, and token.
Revocation uses the DELETE route but retains the agent row as a durable tombstone.
It sets revoked_at and overwrites the token hash. The database change commits
before the brain makes a best-effort attempt to kick active
sockets locally and across replicas. Subsequent authentication with the old
token fails even if a kick is delayed or lost. Worker control happens through the commands API (restart-worker, pool-resize, etc.).
List agents
Section titled “List agents”GET /api/v1/projects/{slug}/agentsRole: viewer. Returns a list of AgentPublic:
[ { "id": "...", "project_id": "...", "name": "web-01", "state": "online", "protocol_version": "2", "framework_adapter": "django", "engine_adapters": ["celery"], "scheduler_adapters": ["celery-beat"], "capabilities": {}, "last_seen_at": "...", "last_connect_at": "...", "created_at": "...", "is_outdated": false }]is_outdated is true when the agent connected at least once and its last advertised protocol_version is older than the brain's CURRENT_PROTOCOL. Never-connected agents report false.
Mint agent (returns token + HMAC secret)
Section titled “Mint agent (returns token + HMAC secret)”POST /api/v1/projects/{slug}/agentsRole: admin. CSRF-protected and requires a fresh MFA verification. The fresh
MFA gate makes minting browser-session-only; an API key is rejected.
{"name": "billing-worker-02"}(project_id, name) is unique among live agents; a duplicate returns 409
with "error": "conflict". Response (shown once -- save both):
{ "agent": { /* AgentPublic */ }, "token": "<43 URL-safe base64 characters; no prefix>", "hmac_secret": "<urlsafe-base64, 32 raw bytes>"}The hmac_secret is the per-project signing key. It is HMAC-derived from the
current brain master secret and is not persisted by the brain. Consequently,
rotating Z4J_SECRET requires re-credentialing every agent. Adding the old
master to Z4J_PREVIOUS_SECRETS keeps the old bearer token acceptable during
the rotation window, but does not make an old hmac_secret valid: the
handshake can succeed and the first signed data frame then fails HMAC.
Operators paste both values into the agent configuration; the agent refuses to
start without hmac_secret.
Revoke agent
Section titled “Revoke agent”DELETE /api/v1/projects/{slug}/agents/{agent_id}Role: admin. CSRF-protected and requires fresh MFA. Soft-revokes the token
and retains the agent row because events.agent_id is non-null and uses
ON DELETE RESTRICT; tasks have no agent foreign key. The tombstone is hidden
from the agent list and cannot reconnect or receive new work. Revocation keeps
the original name initially. Minting the same name later moves that tombstone
into a reserved namespace under its row lock, then inserts a new agent row.
The replacement does not reuse the revoked row or its ID.
Agent health
Section titled “Agent health”GET /api/v1/projects/{slug}/agents/{agent_id}/healthRole: viewer. Returns at most 100 retained status samples, newest first, with
id, captured_at, worker_id and telemetry_loss. captured_at is the time
the agent signed the sample for sending, not the time its counters were read, so
a sample buffered during an outage carries its delivery time. Revoked or foreign
agents return 404. telemetry_loss: null means that sample does not provide supported
loss accounting. An empty list is not a zero-loss measurement.
Counters are cumulative within their buffer_id or runtime_id scope. Do not
sum samples or treat the bounded history as a complete fleet inventory. See
agent telemetry loss for counter
units, retention and operational interpretation.