Documentation Index
Fetch the complete documentation index at: https://docs.wellapp.ai/llms.txt
Use this file to discover all available pages before exploring further.
People is a workspace-scoped entity representing an individual contact — an employee, owner, or other relationship a business interacts with. It is the central node for personal contact data, linked to atomic contact details (emails, phones, locations, web links) through dedicated pivot entities. A People record is connected to one or more Company entities via the CompanyPerson pivot, participates in workspace Membership, and tracks its data-source provenance through the PeopleWorkspaceConnector relation. It is a primary records-page root exposed in the data-views pipeline.
| Naming | Value |
|---|
| Object | People |
Resource type (JSON:API type) | people |
| Collection / records root | people |
| REST base | /v1/people |
| Entity class | People |
API operations
| Operation | Method & path | Status |
|---|
| List | GET /v1/people | ✅ Implemented |
| Retrieve | GET /v1/people/{id} | ✅ Implemented |
| Create | POST /v1/people | ✅ Implemented |
| Update | PATCH /v1/people/{id} | ✅ Implemented |
| Delete | DELETE /v1/people/{id} | ✅ Implemented |
| Enrich | POST /v1/people/enrich | ✅ Implemented |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|
| person_id | string, UUID | ✅ Yes | unique; generated by gen_random_uuid() on insert | — | Public stable identifier for this person record. Used in all API responses and external references. Never expose the internal pk. |
| full_name | string | ✅ Yes | varchar(255), not null | — | Display name for the person. Stored directly on the entity (not derived at query time from first_name + last_name, though both component fields are also persisted). Used as the primary display label across the records UI and composites. |
| first_name | string | ✅ Yes | varchar(255), not null | — | Given (first) name component. Persisted alongside full_name to enable name-part filtering and salutation formatting. |
| last_name | string | ✅ Yes | varchar(255), not null | — | Family (last) name component. Persisted alongside full_name. |
| job_title | string | ⚪ No | varchar(100), nullable | — | Professional role or title of the person at their primary company. Decorated with @Enrichable — the enrichment pipeline may populate or update this field automatically. |
| external_person_id | string | ⚪ No | nullable; partial unique index on (external_person_id, workspace_pk) WHERE deleted_at IS NULL — replaces original global unique constraint (Migration20260216120000). Uniqueness is scoped per workspace. | — | Deduplication key from the originating connector (e.g. a CRM contact ID). Used by the reconciliation pipeline for find-or-create matching: if a sync supplies an external_person_id already known in the workspace, the existing record is updated rather than a duplicate created. NULL for manually created records. |
| created_at | Date, 🔒 system | ✅ Yes | timestamptz, not null; set via onCreate lifecycle hook | — | Timestamp of record creation. Set automatically by MikroORM on first persist; never updated. |
| updated_at | Date, 🔒 system | ⚪ No | timestamptz, nullable; set via onCreate and onUpdate lifecycle hooks | — | Timestamp of last modification. Set on create and updated on every subsequent write by MikroORM. |
| deleted_at | Date | ⚪ No | timestamptz, nullable; null = active record | — | Soft-delete timestamp. When set, the record is treated as deleted across all queries and is excluded from the partial unique index on external_person_id. Never hard-deleted. All repositories must filter deleted_at: null. |
Relationships
| Name | Type | Required | Description |
|---|
| workspace | to-one (Workspace) | ⚪ No (nullable ManyToOne) | The workspace that owns this person record. Provides multi-tenant scope: all repository queries filter by workspace. Nullable to allow global catalog entries, though in practice all active People records have a workspace. |
| source_workspace_connector | to-one (WorkspaceConnector) | ⚪ No (nullable ManyToOne) | The WorkspaceConnector instance that created or last synced this record. NULL for manually created people. Used by the pipeline to track provenance and to surface the connector-logo-name composite in the data-views records page. |
| media | to-one (Media) | ⚪ No (nullable ManyToOne) | Profile photo / avatar for this person. Linked via a direct ManyToOne to the shared Media entity. The overrides.yml marks media.url as editable with display_type: image; write goes through add/delete join-entity mutations, not a scalar PATCH. |
| emails | to-many (PersonEmail) | ⚪ No | Email addresses linked to this person through the PersonEmail pivot entity. Each pivot record carries is_primary, is_verify, and label (work/personal/other). A partial unique index ensures at most one primary email per person (WHERE deleted_at IS NULL AND is_primary IS TRUE). |
| phones | to-many (PersonPhone) | ⚪ No | Phone numbers linked through the PersonPhone pivot entity. Each pivot record carries is_primary, is_verify, and label (mobile/work/personal/other). A partial unique index enforces at most one primary phone per person. Decorated @Enrichable on the People side — enrichment pipeline may populate phone data. |
| locations | to-many (PersonLocation) | ⚪ No | Geographic locations linked through the PersonLocation pivot entity. Each pivot record carries is_primary, is_legal, and label. A partial unique index enforces at most one primary location per person. |
| web_links | to-many (PersonWebLink) | ⚪ No | Social profile or other web links associated with this person, linked through the PersonWebLink pivot entity. Decorated @Enrichable on the People side — enrichment pipeline may populate social links (e.g. LinkedIn). |
| companies | to-many (CompanyPerson) | ⚪ No | Company associations for this person, modelled through the CompanyPerson pivot entity. Each pivot record carries a relationship_type enum (contact / employee / owner / other, default: other). A person may be associated with multiple companies across workspaces. |
| memberships | to-many (Membership) | ⚪ No | Workspace membership records for this person. The Membership entity links People to a Workspace with role and status information for platform access control. |
| collect | to-many (Collect) | ⚪ No | Collect (document collection) records associated with this person. Links the People entity into the document collection pipeline. |
| workspace_connectors | to-many (WorkspaceConnector) | ⚪ No | WorkspaceConnector records whose person field points to this People row. Used to associate a connector instance with the authenticated platform user who installed or owns it. |
| people_workspace_connectors | to-many (PeopleWorkspaceConnector) | ⚪ No | Per-row provenance records linking this People entity to specific WorkspaceConnector instances that have synced it. Each PeopleWorkspaceConnector record carries a direction enum (inbound/outbound) and its own created_at / updated_at / deleted_at. Analogous to CompanyWorkspaceConnector for Companies. |
System-computed
- person_id is generated by PostgreSQL gen_random_uuid() as a column default; unique constraint peoples_person_id_unique enforced at the DB level.
- created_at is set via MikroORM onCreate lifecycle hook (new Date()); never updated after first persist.
- updated_at is set on both onCreate and onUpdate via MikroORM lifecycle hooks; reflects the last modification timestamp.
- deleted_at is null for active records; set to a timestamp on soft-delete. The partial unique index on (external_person_id, workspace_pk) excludes rows where deleted_at IS NOT NULL, allowing soft-deleted records to be re-created with the same external_person_id.
- external_person_id is the deduplication key for connector-driven sync: if a sync provides an external_person_id already present in the workspace (under the partial unique constraint), the reconciliation pipeline updates the existing record rather than creating a new one.
- sourceWorkspaceConnector is set by the sync pipeline at creation time to record which WorkspaceConnector produced this record; NULL for manually created people.
- The @Enrichable decorator on job_title, phones, and webLinks marks these fields as candidates for AI enrichment via the enrichment pipeline (Cloud Tasks workers).
- Two partial indexes are maintained by migrations for the records-page query path: idx_peoples_workspace_deleted (workspace_pk WHERE deleted_at IS NULL) and idx_peoples_workspace_created_active (workspace_pk, created_at DESC WHERE deleted_at IS NULL) for default sort order.
- The composite_avatar_fullname composite field (source_fields: person_id, media.url, full_name; display_type: people_avatar_name) is materialized at query time by the data-views pipeline — it is not a persisted column.
- The sourceWorkspaceConnector.composite_connector_logo_name composite (source_fields: workspace_connector_id, connector.service_id, connector.name; display_type: connector_logo_name) is likewise a query-time composite on the people records root.
Example
{
"data": {
"type": "people",
"id": "a3f1c2d4-7e89-4b10-bcd2-1f234567890a",
"attributes": {
"person_id": "a3f1c2d4-7e89-4b10-bcd2-1f234567890a",
"full_name": "Sophie Marotremy",
"first_name": "Sophie",
"last_name": "Marotremy",
"job_title": "Head of Finance",
"external_person_id": "crm_contact_0049281",
"created_at": "2025-11-03T09:14:22.000Z",
"updated_at": "2026-03-17T14:55:10.000Z",
"deleted_at": null
},
"relationships": {
"workspace": {
"data": { "type": "workspace", "id": "f9e2a1b3-0000-4c2d-8888-aabbccddeeff" }
},
"source_workspace_connector": {
"data": { "type": "workspace_connector", "id": "7a123456-dead-beef-cafe-000000000001" }
},
"media": {
"data": { "type": "media", "id": "bb887766-5544-3322-1100-aabbccddeeff" }
},
"emails": {
"data": [
{ "type": "person_email", "id": "11223344-aaaa-bbbb-cccc-000000000001" }
]
},
"phones": {
"data": [
{ "type": "person_phone", "id": "55667788-aaaa-bbbb-cccc-000000000001" }
]
},
"locations": {
"data": [
{ "type": "person_location", "id": "99aabbcc-aaaa-bbbb-cccc-000000000001" }
]
},
"web_links": {
"data": [
{ "type": "person_web_link", "id": "ddeeff00-aaaa-bbbb-cccc-000000000001" }
]
},
"companies": {
"data": [
{ "type": "company_person", "id": "fa1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d" }
]
},
"memberships": {
"data": []
}
}
}
}
Source: apps/api/src/database/entities/People.ts · domain: financial-graph · tier: Main