PersonEmail

PersonEmail is the pivot relation that links a People record to an Email address within the financial graph. It carries metadata about the association โ€” whether the address is the person's primary contact, whether it has been verified, and a label classifying its context (work, personal, other). A single person may have multiple PersonEmail rows; at most one may carry is_primary = true per person (enforced by a partial unique index). The entity is workspace-scoped indirectly through the People side and is written exclusively by the connector-sync and enrichment pipelines.

NamingValue
ObjectPersonEmail
Resource type (JSON:API type)person_email
Collection / records rootโ€” (not a records root)
REST base/v1/person-emails
Entity classPersonEmail

Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.

API operations

OperationMethod & pathStatus
ListGET /v1/person-emails๐ŸŸก Planned
RetrieveGET /v1/person-emails/{id}๐ŸŸก Planned
CreatePOST /v1/person-emails๐ŸŸก Planned
UpdatePATCH /v1/person-emails/{id}๐ŸŸก Planned
DeleteDELETE /v1/person-emails/{id}๐ŸŸก Planned

Data model

Attributes

FieldTypeRequiredConstraintsAllowed valuesDescription
is_primarybooleanโšช NoDEFAULT false; partial unique index uniq_person_emails_primary_person enforces at-most-one primary per person among non-deleted rowstrue | falseWhether this email address is the person's primary contact address. At most one PersonEmail per person may have is_primary = true while deleted_at IS NULL (enforced by partial unique index uniq_person_emails_primary_person on person_pk WHERE deleted_at IS NULL AND is_primary IS TRUE).
is_verifybooleanโšช NoDEFAULT falsetrue | falseWhether the email address has been verified for this person. Set by the enrichment or connector-sync pipeline; not validated via a user-facing flow.
label๐Ÿ”’ system โ€” PersonEmailLabelEnum (native PG enum person_email_label_enum)โœ… YesDEFAULT 'other'; native enum person_email_label_enum โ€” stored values are the enum VALUE strings'work' | 'personal' | 'other'Classifies the context of this email address for the person. Stored as a native PostgreSQL enum introduced in Migration20251204160141.
created_at๐Ÿ”’ system โ€” Date (timestamptz)โœ… YesNOT NULL; set on insert via onCreate: () => new Date()โ€”Timestamp when the pivot row was created. Set by the MikroORM onCreate hook; never updated.
deleted_atDate | null (timestamptz)โšช NoNULLABLE; no default (equivalent to null)โ€”Soft-delete timestamp. Null when the row is active. All queries must filter deleted_at IS NULL. The partial index idx_person_emails_person and the primary-person uniqueness index both scope to deleted_at IS NULL.

Relationships

NameTypeRequiredDescription
personto-one (ManyToOne)โœ… YesThe People record that owns this email address. Foreign key person_pk references core_api.peoples(pk) ON UPDATE CASCADE. An index idx_person_emails_person covers person_pk WHERE deleted_at IS NULL for reverse-traversal hot-path queries.
emailto-one (ManyToOne)โœ… YesThe Email atomic record containing the address string. Foreign key email_pk references core_api.emails(pk) ON UPDATE CASCADE. An index idx_person_emails_email covers the email_pk column to support the Hasura emails โ†’ person_emails array relationship traversal.

System-computed

  • created_at โ€” set on insert via MikroORM onCreate hook; never updated (no updated_at column on this entity)
  • deleted_at โ€” soft-delete; set to current timestamp by the pipeline or service that removes the association; queries must always predicate deleted_at IS NULL
  • is_primary uniqueness โ€” enforced by partial unique index uniq_person_emails_primary_person (person_pk) WHERE deleted_at IS NULL AND is_primary IS TRUE; the pipeline is responsible for demoting any prior primary before promoting a new one
  • label default โ€” MikroORM entity-level default PersonEmailLabelEnum.OTHER ('other') applied at construction; mirrored by database DEFAULT 'other' set in Migration20251204160141
  • is_primary / is_verify defaults โ€” entity-level TypeScript defaults (false) mirrored by database DEFAULT false set in Migration20251204160141
  • No UUID public identifier โ€” PersonEmail has no *_id UUID column; it is not directly addressable via the public API as a standalone resource; it is always accessed through its parent People or Email relationship

Example

{
  "data": {
    "type": "person_email",
    "id": null,
    "attributes": {
      "is_primary": true,
      "is_verify": false,
      "label": "work",
      "created_at": "2025-11-14T09:22:07.000Z",
      "deleted_at": null
    },
    "relationships": {
      "person": {
        "data": { "type": "people", "id": "7e4c2f91-3b8a-4d56-9f1e-bc23047a8e12" }
      },
      "email": {
        "data": { "type": "email", "id": "a1d0c29b-5e3f-4812-8b7a-fc901234abcd" }
      }
    }
  }
}
Source: /Users/maximechampoux/platform/apps/api/src/database/entities/PersonEmail.ts ยท domain: financial-graph ยท tier: Supporting