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.
Phone is a shared atomic resource representing a single telephone number stored in its canonical decomposed form: ITU-T country-calling-code, national-number string, and the derived E.164 number. It is linked to People and Company records through pivot entities (PersonPhone, CompanyPhone) that carry relationship-level metadata such as primary/verified status and a label. A Phone always belongs to a workspace via a nullable integer FK (workspace_pk) added in Migration20260119180000 and is soft-deletable. It is exposed as a records root named “phones” in the data-views pipeline with composite columns resolving linked companies and people.
| Naming | Value |
|---|
| Object | Phone |
Resource type (JSON:API type) | phone |
| Collection / records root | phones |
| REST base | /v1/phones |
| Entity class | Phone |
API operations
| Operation | Method & path | Status |
|---|
| List | GET /v1/phones | ✅ Implemented |
| List (nested) | GET /v1/people/{id}/phones | 🟡 Planned |
| Retrieve | GET /v1/phones/{id} | ✅ Implemented |
| Create (nested) | POST /v1/people/{id}/phones | ✅ Implemented |
| Update | PATCH /v1/phones/{id} | 🟡 Planned |
| Delete (nested) | DELETE /v1/people/{id}/phones/{subId} | ✅ Implemented |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|
| phone_id | string, UUID, 🔒 system | ✅ Yes | unique; generated by gen_random_uuid() on INSERT | — | Public stable identifier for the phone number. Used in all API responses and cross-resource references. The internal pk is never exposed. |
| country_code | integer | ✅ Yes | NOT NULL | Any valid ITU-T calling code (e.g. 1, 33, 44, 49) | The international dialing prefix without the leading ’+’. For example France = 33, US = 1, UK = 44. |
| national_number | string | ✅ Yes | varchar(255); NOT NULL | — | The national (subscriber) portion of the phone number, without the country code prefix and without leading zeros stripped. Stored as a string to preserve leading zeros for countries that require them. |
| e164_number | string | ✅ Yes | varchar(255); NOT NULL | — | The fully-qualified E.164 representation of the number, including the leading ’+’ and country code. This is the canonical machine-readable form used for display, deduplication, and connector sync matching. |
| created_at | datetime, 🔒 system | ✅ Yes | NOT NULL; set once on INSERT via onCreate lifecycle hook | — | Timestamp at which the Phone row was created. Immutable after creation. |
| updated_at | datetime, 🔒 system | ⚪ No | nullable; set on INSERT and updated on every UPDATE via onUpdate lifecycle hook | — | Timestamp of the last modification to the Phone row. Null if the row has never been updated after creation. |
| deleted_at | datetime | ⚪ No | nullable; null = active record | — | Soft-delete timestamp. When set, the phone number is considered deleted and all queries must filter deleted_at IS NULL. Hard-deletes are not used. |
Relationships
| Name | Type | Required | Description |
|---|
| workspace | to-one (workspace) | ⚪ No (nullable) | The workspace that owns this phone number. @ManyToOne(() => Workspace, { nullable: true }). The DB column is workspace_pk (int FK → core_api.workspaces.pk, ON DELETE SET NULL), added by Migration20260119180000. Provides tenant isolation for Hasura RLS filtering. |
| person_phones | to-many (person_phone) | — | Pivot entities linking this phone to one or more People records. Each PersonPhone carries is_primary (with a partial-unique constraint: only one primary per person while deleted_at IS NULL), is_verify, label (PersonPhoneLabelEnum: mobile | work | personal | other), and created_at. Reverse-traversal index idx_person_phones_phone is on phone_pk. |
| company_phones | to-many (company_phone) | — | Pivot entities linking this phone to one or more Company records. Each CompanyPhone carries is_primary (partial-unique: one primary per company while deleted_at IS NULL), is_verify, label (free-text string), and created_at. Bridge-table indexes: idx_company_phones_phone (phone_pk), idx_company_phones_company_deleted (company_pk, deleted_at). |
System-computed
- phone_id is generated by gen_random_uuid() at the database level on INSERT and carries a UNIQUE constraint. It is the public API identifier; the internal pk (serial int) is never exposed.
- created_at is set via MikroORM onCreate lifecycle hook (new Date()) and is immutable thereafter.
- updated_at is set on both INSERT (onCreate) and every UPDATE (onUpdate) via lifecycle hooks.
- deleted_at is null by default. Setting it to a non-null timestamp performs a soft-delete. All reads must include a deleted_at IS NULL predicate. Hard-deletes are not used for Phone rows.
- workspace FK column in the DB is
workspace_pk (int, nullable FK → core_api.workspaces.pk, ON DELETE SET NULL), added by Migration20260119180000. Pre-migration rows have workspace_pk = NULL. The entity declares the relation nullable: true. There is no UUID workspace_id column on the phones table.
- The PersonPhone pivot enforces a partial unique index (uniq_person_phones_primary_person) so that at most one PersonPhone per person_pk has is_primary = TRUE while deleted_at IS NULL.
- The CompanyPhone pivot enforces a parallel partial unique index (uniq_company_phones_primary_company) so that at most one CompanyPhone per company_pk has is_primary = TRUE while deleted_at IS NULL.
- In the data-views pipeline, the phones root exposes two composite columns: composite_companies_list (display_type: relation_list, source_fields: company_phones.company.company_id + company_phones.company.name) and composite_people_list (display_type: relation_list, source_fields: person_phones.people.person_id + person_phones.people.full_name). These are defined in composites.yml under the phones root.
- On company and person records roots, composite_phones_list composites surface linked phone numbers (source_fields: .phone.phone_id + .phone.e164_number) for display in the records table.
Example
{
"data": {
"type": "phone",
"id": "a3f7c291-84e2-4d55-9fc3-b8120e4a7301",
"attributes": {
"phone_id": "a3f7c291-84e2-4d55-9fc3-b8120e4a7301",
"country_code": 33,
"national_number": "612345678",
"e164_number": "+33612345678",
"created_at": "2025-09-14T10:22:00.000Z",
"updated_at": "2026-01-07T08:05:00.000Z",
"deleted_at": null
},
"relationships": {
"workspace": {
"data": { "type": "workspace", "id": "f1a2b3c4-0000-0000-0000-000000000001" }
},
"person_phones": {
"data": [
{ "type": "person_phone", "id": "pivot-pk-91" }
]
},
"company_phones": {
"data": []
}
}
}
}
Source: apps/api/src/database/entities/Phone.ts · domain: financial-graph · tier: Supporting