> ## 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

> Phone is a shared atomic resource representing a single telephone number stored in its canonical decomposed form: ITU-T country-calling-code, national-number st

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: {bridge}.phone.phone\_id + {bridge}.phone.e164\_number) for display in the records table.

## Example

```json theme={null}
{
  "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": []
      }
    }
  }
}
```

<sub>Source: `apps/api/src/database/entities/Phone.ts` · domain: financial-graph · tier: Supporting</sub>
