> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crewship.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Presence

> Watch Roster -- live agent status across a workspace or crew.

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](/guides/watch-roster).

<Note>
  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).
</Note>

## Endpoints

| Method | Endpoint                             | Purpose                                      |
| ------ | ------------------------------------ | -------------------------------------------- |
| GET    | [`/api/v1/presence/roster`](#roster) | Live agent status across a workspace or crew |

***

## Roster

```
GET /api/v1/presence/roster?crew_id=<optional>
```

**Query parameters:**

| Param     | Type   | Description                                                          |
| --------- | ------ | -------------------------------------------------------------------- |
| `crew_id` | string | Optional. Filter to a single crew. Cross-tenant crew IDs return 404. |

**Response:** `200 OK`

```json theme={null}
{
  "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
}
```

| Field             | Type    | Description                                                             |
| ----------------- | ------- | ----------------------------------------------------------------------- |
| `rows[].agent_id` | string  | Agent identifier.                                                       |
| `rows[].crew_id`  | string? | Crew containing the agent. Omitted when not yet associated with a crew. |
| `rows[].status`   | string  | `online`, `busy`, `blocked`, or `offline`.                              |
| `rows[].since`    | string  | RFC3339Nano. Wall-clock time of the last status write.                  |
| `rows[].details`  | object? | Status-specific metadata. Omitted when empty.                           |

**Errors:**

| Status | Condition                                                                                                       |
| ------ | --------------------------------------------------------------------------------------------------------------- |
| 400    | No `workspace_id` resolved for the session (the `RequireWorkspace` middleware rejects before the handler runs). |
| 401    | Not authenticated.                                                                                              |
| 403    | Authenticated, but not a member of the resolved workspace.                                                      |
| 404    | `crew_id` filter points to a crew outside the caller's workspace.                                               |
| 500    | DB error.                                                                                                       |

## Details field conventions

| Status    | Common keys                                                                                                   |
| --------- | ------------------------------------------------------------------------------------------------------------- |
| `online`  | *(usually empty)*                                                                                             |
| `busy`    | `current_task_id`, `current_mission_id`, `started_at`                                                         |
| `blocked` | `blocked_reason` (`awaiting_approval`, `awaiting_keeper`, `awaiting_user_input`), `approval_id` when relevant |
| `offline` | `reason` (`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.

## Related

* [Watch Roster guide](/guides/watch-roster).
* [`crewship presence`](/cli/presence).
* [Crew Journal](/guides/crew-journal) -- `agent.status_change` history.
