# GET /v1/ship/agents and /v1/ship/sessions
Source: https://docs.treeship.dev/api/ship-registry

> Per-ship registry endpoints. List the calling dock's agents and session history.

Two read endpoints return per-ship index data that is populated as a side effect of [`PUT /v1/receipt/{session_id}`](/docs/api/receipt-put). Every uploaded receipt refreshes the calling dock's entries in both.

Both endpoints accept either DPoP authentication (for CLI callers) or a workspace share token (for browser callers). See the [Hub API overview](/docs/api/overview) for auth details.

## GET /v1/ship/agents

Returns the agent registry for the resolved dock, ordered by most recently seen.

### Request

```
GET /v1/ship/agents
Authorization: DPoP dck_9f8e7d6c
DPoP: eyJhbGciOiJFZERTQSJ9...
```

Or, with a workspace share token:

```
GET /v1/ship/agents?session=TOKEN
```

### Response

```json
{
  "agents": [
    {
      "id": "ai_root_1",
      "label": "claude-code",
      "role": "planner",
      "model": "",
      "host": "host_macbookpro",
      "status": "completed"
    },
    {
      "id": "ai_review_2",
      "label": "hermes",
      "role": "reviewer",
      "model": "",
      "host": "host_vps1",
      "status": "completed"
    }
  ]
}
```

| Field    | Type   | Description                                                                            |
| -------- | ------ | -------------------------------------------------------------------------------------- |
| `id`     | string | Stable agent instance id (matches `agent_graph.nodes[].agent_instance_id` in receipts) |
| `label`  | string | Human-readable name, derived from `agent_name` in the most recent receipt              |
| `role`   | string | Role hint (for example `planner`, `reviewer`, `operator`)                              |
| `model`  | string | Reserved for future use; currently empty                                               |
| `host`   | string | Host id where the agent was last observed                                              |
| `status` | string | Last observed status (`completed`, `failed`, or empty for still-active)                |

Returns an empty list if the dock has never uploaded a receipt.

## GET /v1/ship/sessions

Returns the calling dock's session list, ordered `uploaded_at DESC` then `started_at DESC`.

### Request

```
GET /v1/ship/sessions
Authorization: DPoP dck_9f8e7d6c
DPoP: eyJhbGciOiJFZERTQSJ9...
```

Or with a share token:

```
GET /v1/ship/sessions?session=TOKEN
```

### Response

```json
{
  "sessions": [
    {
      "session_id": "ssn_42e740bd9eb238f6",
      "name": "fix auth bug",
      "started_at": "2026-04-09T06:57:56Z",
      "ended_at": "2026-04-09T07:04:38Z",
      "duration_min": 6,
      "status": "completed",
      "agent_count": 1,
      "action_count": 2,
      "receipt_url": "https://treeship.dev/receipt/ssn_42e740bd9eb238f6"
    },
    {
      "session_id": "ssn_8a3f1e...",
      "name": "deploy v2.3",
      "started_at": "2026-04-08T14:00:00Z",
      "ended_at": "2026-04-08T14:42:01Z",
      "duration_min": 42,
      "status": "completed",
      "agent_count": 3,
      "action_count": 18,
      "receipt_url": "https://treeship.dev/receipt/ssn_8a3f1e..."
    }
  ]
}
```

| Field          | Type    | Description                                                      |
| -------------- | ------- | ---------------------------------------------------------------- |
| `session_id`   | string  | Session id                                                       |
| `name`         | string  | Human-readable session name if one was set                       |
| `started_at`   | string  | ISO 8601 timestamp when the session started                      |
| `ended_at`     | string  | ISO 8601 timestamp when the session closed (empty if still open) |
| `duration_min` | integer | Session duration in minutes, derived from `duration_ms / 60000`  |
| `status`       | string  | `open`, `closed`, `completed`, `failed`, or `abandoned`          |
| `agent_count`  | integer | Number of distinct agents in the receipt's participants          |
| `action_count` | integer | Number of timeline events in the receipt                         |
| `receipt_url`  | string  | Permanent public URL for the receipt (empty for open sessions)   |

Open sessions appear in the list if the hub has any row for them, even without an uploaded receipt. Closed sessions with an uploaded receipt carry a non-empty `receipt_url`.

## Authentication notes

Both endpoints go through `auth.ResolveReader`, which:

1. Checks for a `?session=TOKEN` query parameter first. Valid tokens resolve to the dock id bound at mint time.
2. Falls back to DPoP proof in the `Authorization` header.
3. Fails closed: a present-but-invalid share token returns 401 rather than falling through to DPoP, so stale share links produce clear errors instead of confusing "missing Authorization" messages.

<Callout type="info">
  The registry tables are derived indexes. The source of truth is the receipt itself. If you need the full agent graph, timeline, or Merkle proofs, fetch the receipt at [`GET /v1/receipt/{session_id}`](/docs/api/receipt-get).
</Callout>