Skip to main content
The Presence API powers the Watch Roster — a live view of every agent’s status across a workspace or a single crew. It is read-only: status is owned by the system and written by the agent runtime on lifecycle transitions, so there is no POST surface. See the Watch Roster guide.
The single read endpoint requires authentication and is workspace-scoped. A crew_id filter pointing outside the caller’s workspace returns 404 (same shape as not-found, so existence isn’t leaked).

Endpoints

MethodEndpointPurpose
GET/api/v1/presence/rosterLive agent status across a workspace or crew

Roster

GET /api/v1/presence/roster?crew_id=<optional>
Query parameters:
ParamTypeDescription
crew_idstringOptional. Filter to a single crew. Cross-tenant crew IDs return 404.
Response: 200 OK
{
  "rows": [
    {
      "agent_id": "agt_viktor",
      "crew_id": "crw_backend",
      "status": "busy",
      "since": "2026-04-17T10:23:41.000Z",
      "details": {
        "current_task_id": "T-42"
      }
    },
    {
      "agent_id": "agt_eva",
      "crew_id": "crw_backend",
      "status": "blocked",
      "since": "2026-04-17T10:05:33.000Z",
      "details": {
        "blocked_reason": "awaiting_approval",
        "approval_id": "req_01HXYZABCDEF"
      }
    }
  ],
  "count": 2
}
FieldTypeDescription
rows[].agent_idstringAgent identifier.
rows[].crew_idstring?Crew containing the agent. Omitted when not yet associated with a crew.
rows[].statusstringonline, busy, blocked, or offline.
rows[].sincestringRFC3339Nano. Wall-clock time of the last status write.
rows[].detailsobject?Status-specific metadata. Omitted when empty.
Errors:
StatusCondition
400No workspace_id resolved for the session (the RequireWorkspace middleware rejects before the handler runs).
401Not authenticated.
403Authenticated, but not a member of the resolved workspace.
404crew_id filter points to a crew outside the caller’s workspace.
500DB error.

Details field conventions

StatusCommon keys
online(usually empty)
busycurrent_task_id, current_mission_id, started_at
blockedblocked_reason (awaiting_approval, awaiting_keeper, awaiting_user_input), approval_id when relevant
offlinereason (idle_timeout when the sweeper flipped the row)
Schema is loose — the orchestrator stamps what is useful at the transition point.

Writes

There is no POST endpoint. Status is owned by the system; operators influence it only indirectly by starting/stopping agents. Internal writers:
  • presenceAdapter.Track (orchestrator) -> presence.Upsert on lifecycle transitions.
  • presence.SweepOffline (server boot wires a 60s ticker) flips idle agents past the 5-min threshold.