# Route Spec

## Route ID
`hubs-members-list`

## Endpoint
`GET /api/v1/hubs/{hubId}/members`

## Human Description
Lists hub members with local search. Active members may receive phone numbers. Invited or pending-approval viewers may use this as a dark-hub member preview, but phone numbers are not returned for them.
Pending invitations and pending admin-approval requests are not active members and are not returned here.

## Authentication
- Required: `yes`

## Request
### Query Parameters
- `q` (`string`, optional): local search in this member list.
- `role` (`string`, optional, enum `member|admin|super_admin`)
- `cursor` (`string`, optional)
- `limit` (`number`, optional, default `20`, max `100`)

## Responses
### Success: `200 OK`
```json
{"success": true, "message": "Members loaded", "data": {"items": [{"id": "usr_1", "name": "Sara Ahmed", "username": "sara", "profilePhotoUrl": "https://cdn.example.com/u/1/profile.jpg", "phoneNumber": "+1234567890"}], "nextCursor": null}}
```

### Error: `401 Unauthorized`
When returned:
- Missing or invalid access token.

Body:
```json
{"success": false, "error": {"code": "UNAUTHORIZED", "message": "Authentication required.", "details": {}}}
```

### Error: `403 Forbidden`
When returned:
- User has no active, invited, or pending-approval relationship to this hub.

Body:
```json
{"success": false, "error": {"code": "HUB_MEMBERSHIP_REQUIRED", "message": "Hub relationship required.", "details": {}}}
```

### Error: `404 Not Found`
When returned:
- Hub does not exist.

Body:
```json
{"success": false, "error": {"code": "HUB_NOT_FOUND", "message": "Hub does not exist.", "details": {}}}
```

## Data & Caching Dependencies
- **Spanner Tables:** `hub_memberships, users (Read)`
- **Redis Cache:** `None`
- **GCS Storage:** `None`
- **Edge Cache (CDN):** `No`

## Side Effects
- None (read-only endpoint).

## Privacy Notes
- This endpoint requires either active membership or a dark/pending relationship to the hub.
- Phone numbers returned here are not part of the public profile contract.
- Phone numbers are populated only for active approved members. For invited or pending-approval viewers, `phoneNumber` must be `null`.
- Pending/dark invitees are listed through `GET /api/v1/hubs/{hubId}/invitations` with masked phone numbers.
