List People
Page through a workspace's people. Filtering and sorting (including across related objects) is done through the records query.
/v1/peopleReturns a paginated list of people in the caller's workspace with their relationships. Read-only; no side effects. Includes both platform users (with memberships) and CRM contacts. ## Filtering & sorting Filter via `POST /v1/records/query` (root `people`) or the `filters` model; operators are gated by each field's data type. See [Filtering & sorting](../filtering-and-sorting). **Filterable fields** | Field | Type | Allowed operators | |---|---|---| | `composite_accounts_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_avatar_fullname` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_cards_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_checks_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_emails_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_locations_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_media_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_memberships_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_payment_means_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_phones_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_routed_to` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_sourced_from` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_web_links_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `composite_workspace_connectors_list` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `created_at` | date | `eq` `lt` `gt` `is_null` `is_not_null` | | `deleted_at` | date | `eq` `lt` `gt` `is_null` `is_not_null` | | `external_person_id` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `first_name` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `full_name` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `job_title` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `last_name` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `person_id` | text | `contains` `eq` `neq` `starts_with` `ends_with` `is_null` `is_not_null` | | `pk` | number | `eq` `neq` `gt` `gte` `lt` `lte` `is_null` `is_not_null` | | `updated_at` | date | `eq` `lt` `gt` `is_null` `is_not_null` | **Sortable fields:** `composite_accounts_list`, `composite_avatar_fullname`, `composite_cards_list`, `composite_checks_list`, `composite_emails_list`, `composite_locations_list`, `composite_media_list`, `composite_memberships_list`, `composite_payment_means_list`, `composite_phones_list`, `composite_routed_to`, `composite_sourced_from`, `composite_web_links_list`, `composite_workspace_connectors_list`, `created_at`, `deleted_at`, `external_person_id`, `first_name`, `full_name`, `job_title`, `last_name`, `person_id`, `pk`, `updated_at` (via `orderBy` / `sort`, `asc`|`desc`).
Requires a bearer token: Authorization: Bearer <token>.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | string | No | 1-based page number for offset pagination.e.g. 1 |
| limit | string | No | Page size, 1 to 100.e.g. 25 |
Request
curl -X GET https://api.wellapp.ai/v1/people \
-H "Authorization: Bearer $WELL_API_TOKEN"Responses
200 — A page of people with pagination metadata.
{
"data": [
{
"type": "people",
"id": "c4d5e6f7-8a9b-40c1-92d3-e4f5a6b7c8d9",
"attributes": {
"full_name": "Remy Okafor",
"first_name": "Remy",
"last_name": "Okafor",
"job_title": "Founder and CEO",
"created_at": "2026-01-12T09:31:00.000Z",
"updated_at": "2026-05-18T11:22:09.000Z"
},
"relationships": {
"companies": {
"data": [
{
"type": "companies",
"id": "b2c3d4e5-6f70-4812-93a4-b5c6d7e8f9a0"
}
]
}
}
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 25,
"total": 4
}
}
}401 — Unauthorized
{
"code": "UNAUTHORIZED",
"status": 401,
"title": "Unauthorized",
"message": "See title.",
"meta": {
"trace_id": "a1b2c3",
"log_id": "a1b2c3"
}
}403 — Forbidden
{
"code": "FORBIDDEN",
"status": 403,
"title": "Forbidden",
"message": "See title.",
"meta": {
"trace_id": "a1b2c3",
"log_id": "a1b2c3"
}
}GET /v1/people returns the workspace's people (contacts), page-paginated with page and limit (max 100). It does not accept inline filtering or sorting — those query params are ignored.
To filter or sort people — including across related objects — use the records query: POST /v1/records/query with root: "people". It exposes the full filters / raw whereClause and orderBy model. For the operator set and pagination notes see Filtering & sorting.
Filtering & sorting on related objects
This resource's related objects (the ones you can include) and how to filter or sort on each:
| Relationship | Cardinality | Filter on a field | Sort by a field |
|---|---|---|---|
workspace | to-one (workspace) | { "workspace": { "name": { "_eq": … } } } | "field": "workspace.name" ✅ |
source_workspace_connector | to-one (workspaceconnector) | { "source_workspace_connector": { "field_name": { "_eq": … } } } | "field": "source_workspace_connector.field_name" ✅ |
media | to-one (media) | { "media": { "file_name": { "_eq": … } } } | "field": "media.file_name" ✅ |
emails | to-many (personemail) | { "emails": { "_some": { "address": { "_eq": … } } } } | aggregate proxy only ⚠️ |
phones | to-many (personphone) | { "phones": { "_some": { "number": { "_eq": … } } } } | aggregate proxy only ⚠️ |
locations | to-many (personlocation) | { "locations": { "_some": { "city": { "_eq": … } } } } | aggregate proxy only ⚠️ |
web_links | to-many (personweblink) | { "web_links": { "_some": { "field_name": { "_eq": … } } } } | aggregate proxy only ⚠️ |
companies | to-many (companyperson) | { "companies": { "_some": { "field_name": { "_eq": … } } } } | aggregate proxy only ⚠️ |
memberships | to-many (membership) | { "memberships": { "_some": { "role": { "_eq": … } } } } | aggregate proxy only ⚠️ |
collect | to-many (collect) | { "collect": { "_some": { "field_name": { "_eq": … } } } } | aggregate proxy only ⚠️ |
workspace_connectors | to-many (workspaceconnector) | { "workspace_connectors": { "_some": { "field_name": { "_eq": … } } } } | aggregate proxy only ⚠️ |
people_workspace_connectors | to-many (peopleworkspaceconnector) | { "people_workspace_connectors": { "_some": { "field_name": { "_eq": … } } } } | aggregate proxy only ⚠️ |
Replace field_name with any field of the related object. See its object-reference page for the full field list.
Filter by a to-one relation (and sort by it):
{
"root": "people",
"whereClause": { "workspace": { "name": { "_ilike": "%acme%" } } },
"orderBy": { "field": "workspace.name", "direction": "asc" }
}Filter by a to-many relation (quantified — bare nesting is invalid):
{
"root": "people",
"whereClause": { "emails": { "_some": { "address": { "_ilike": "%@acme.com" } } } }
}