---
id: DOM-CRM-ENT-001
title: Contact
realises: CON-CRM-ENT-001
product_area: crm
module: entities
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
owned_by: SVC-CRM-ENT-entities
lifecycle: STATE-CRM-ENT-001
custom_fields: declared
identity: >-
  Contacts have workspace-scoped identities. Creation keeps a valid unused
  client identity or generates one. The service allocates a separate
  staff-facing contact number; clients cannot choose it, and allocated numbers
  are not recycled.
attributes:
  - name: id
    kind: text
    required: true
    computed: false
    set_by: integration
    rules: >-
      Unused workspace identity with letters, digits, underscores, hyphens or
      vertical bars; generated when omitted.
    example: ruth_kim
    notes: A continuing identity for integrations and record references.
  - name: number
    kind: number
    required: true
    computed: true
    set_by: system
    rules: >-
      Assigned by the workspace contact counter; never chosen or recycled by a
      client.
    example: 10428
    notes: The staff-facing contact reference.
  - name: status
    kind: enum
    required: true
    computed: false
    set_by: staff
    rules: >-
      Active is the default; active, inactive, deceased and duplicate are the
      closed choices.
    example: active
    notes: The person’s current business classification of availability.
  - name: contact type
    kind: reference
    required: false
    computed: false
    set_by: staff
    rules: >-
      Optional workspace contact type; retained inactive references remain
      valid.
    example: Member
    notes: The workspace category that selects effective forms and layouts.
  - name: prefix
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Trimmed optional text up to 200 characters; first or last must contain
      text.
    example: Dr.
    notes: Title appearing before the full name.
  - name: salutation
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Trimmed optional text; cannot replace both first and last names.
    example: Dear Ruth
    notes: How correspondence addresses the person.
  - name: first name
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Trimmed optional text up to 200 characters; first or last must contain
      text.
    example: Ruth
    notes: Given name used in display.
  - name: middle name
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Trimmed optional text up to 200 characters; first or last must contain
      text.
    example: Anne
    notes: Middle name reduced to an initial in derived names.
  - name: last name
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Trimmed optional text up to 200 characters; first or last must contain
      text.
    example: Kim
    notes: Family name used for display and sorting.
  - name: maiden name
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Trimmed optional text; cannot replace both first and last names.
    example: Park
    notes: Earlier family name retained as personal information.
  - name: nickname
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Trimmed optional text up to 200 characters; first or last must contain
      text.
    example: Roo
    notes: Familiar name included in derived presentation.
  - name: suffix
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Trimmed optional text up to 200 characters; first or last must contain
      text.
    example: Jr.
    notes: Name suffix.
  - name: credentials
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Trimmed optional text; cannot replace both first and last names.
    example: CAE
    notes: Professional qualifications following the display name.
  - name: display name
    kind: computed
    required: true
    computed: true
    set_by: system
    rules: >-
      Rebuilt from name parts on writes; direct submitted derived names are
      refused.
    example: 'Ruth A. Kim, CAE'
    notes: The compact name used in ordinary record presentation.
  - name: full name
    kind: computed
    required: true
    computed: true
    set_by: system
    rules: Computed alongside the other derived names.
    example: 'Dr. Ruth A. Kim, CAE'
    notes: The display name with its prefix.
  - name: sort name
    kind: computed
    required: true
    computed: true
    set_by: system
    rules: Computed from the same supplied name components.
    example: 'Kim, Ruth A.'
    notes: The family-first label for ordering people.
  - name: job title
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Trimmed optional text; saved contact and primary-affiliation titles are
      coordinated.
    example: Executive Director
    notes: The title associated with the prominent organizational connection.
  - name: spouse
    kind: reference
    required: false
    computed: false
    set_by: staff
    rules: >-
      Another contact in this workspace; cannot be self or already paired to
      another person.
    example: Daniel Kim
    notes: An interim reciprocal personal connection.
  - name: photo
    kind: reference
    required: false
    computed: false
    set_by: staff
    rules: >-
      Platform file reference made permanent before persistence; display links
      are returned on read.
    example: Ruth’s portrait
    notes: The person’s image for record presentation.
  - name: gender
    kind: enum
    required: false
    computed: false
    set_by: staff
    rules: >-
      The observed closed choices are male and female; broader product
      vocabulary is not established.
    example: female
    notes: Optional stored gender information.
  - name: pronouns
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Optional trimmed text.
    example: she/her
    notes: The person’s stated pronouns.
  - name: date of birth
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: 'Optional ISO calendar date, without a time.'
    example: '1984-04-12'
    notes: The person’s birth date when known.
  - name: date of death
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Optional ISO calendar date; separate from selecting deceased status.
    example: '2026-03-01'
    notes: The recorded death date when known.
  - name: notes
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: 'Trimmed text up to 10,000 characters.'
    example: Prefers calls after the monthly board meeting.
    notes: Free text that staff retain about the person.
  - name: Facebook
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Optional trimmed text; no verified guarantee that the remote profile
      exists.
    example: 'https://www.facebook.com/ruth.kim'
    notes: The recorded social-profile reference.
  - name: Twitter
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Optional trimmed text; no verified guarantee that the remote profile
      exists.
    example: '@RuthKim'
    notes: The recorded social-profile reference.
  - name: Instagram
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Optional trimmed text; no verified guarantee that the remote profile
      exists.
    example: '@ruthkim'
    notes: The recorded social-profile reference.
  - name: LinkedIn
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Optional trimmed text; no verified guarantee that the remote profile
      exists.
    example: 'https://www.linkedin.com/in/ruth-kim'
    notes: The recorded social-profile reference.
  - name: profile last updated
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Optional ISO timestamp accepted by the record schema.
    example: '2026-10-20T14:00:00Z'
    notes: The stored time of profile review.
  - name: social profile last updated
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Optional ISO timestamp.
    example: '2026-10-20T14:00:00Z'
    notes: The stored time of social-profile review.
  - name: region
    kind: reference
    required: false
    computed: true
    set_by: system
    rules: >-
      Regions assigns it unless automatic assignment is overridden; reference
      stays workspace-local.
    example: Western Region
    notes: The regional grouping selected from the deciding address.
  - name: override automatic region assignment
    kind: enum
    required: false
    computed: false
    set_by: staff
    rules: Optional boolean accepted on the person record.
    example: true
    notes: Whether an explicit region choice takes precedence.
  - name: national district
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: >-
      Metadata presents this as read-only; API enforcement is an existing
      unresolved finding.
    example: CA-12
    notes: The label or reference supplied by district lookup.
  - name: national district OCD reference
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: >-
      Metadata presents this as read-only; API enforcement is an existing
      unresolved finding.
    example: 'ocd-division/country:us/state:ca/cd:12'
    notes: The label or reference supplied by district lookup.
  - name: state upper district
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: >-
      Metadata presents this as read-only; API enforcement is an existing
      unresolved finding.
    example: California Senate District 7
    notes: The label or reference supplied by district lookup.
  - name: state upper district OCD reference
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: >-
      Metadata presents this as read-only; API enforcement is an existing
      unresolved finding.
    example: 'ocd-division/country:us/state:ca/sldu:7'
    notes: The label or reference supplied by district lookup.
  - name: state lower district
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: >-
      Metadata presents this as read-only; API enforcement is an existing
      unresolved finding.
    example: California Assembly District 18
    notes: The label or reference supplied by district lookup.
  - name: state lower district OCD reference
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: >-
      Metadata presents this as read-only; API enforcement is an existing
      unresolved finding.
    example: 'ocd-division/country:us/state:ca/sldl:18'
    notes: The label or reference supplied by district lookup.
  - name: do not mail
    kind: enum
    required: true
    computed: false
    set_by: staff
    rules: >-
      Boolean defaults to false; storage does not prove every downstream sender
      honors it.
    example: true
    notes: A broad postal restriction recorded for the person.
  - name: do not call
    kind: enum
    required: true
    computed: false
    set_by: staff
    rules: >-
      Boolean defaults to false; storage does not prove every downstream sender
      honors it.
    example: false
    notes: A broad telephone restriction recorded for the person.
  - name: email opt-out
    kind: enum
    required: true
    computed: false
    set_by: staff
    rules: >-
      Boolean defaults to false; storage does not prove every downstream sender
      honors it.
    example: true
    notes: A broad email restriction recorded for the person.
  - name: communication preferences last confirmed
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Optional ISO timestamp.
    example: '2026-10-22T13:00:00Z'
    notes: When the person’s choices are last reviewed.
  - name: email addresses
    kind: collection
    required: false
    computed: false
    set_by: staff
    rules: >-
      At most three canonical lowercase addresses, each up to 254 characters;
      primary reservation depends on status.
    example:
      - ruth@harbor.example
      - ruth.kim@example.org
    notes: 'Ordered destinations, with the first used as primary.'
  - name: phone numbers
    kind: collection
    required: false
    computed: false
    set_by: staff
    rules: >-
      One per type, one preferred at most; stored source distinguishes owned,
      copied, linked and disconnected values.
    example: 'Work: +1 510 555 0134, extension 204'
    notes: 'Typed numbers with optional extension, preference and source.'
  - name: addresses
    kind: collection
    required: false
    computed: false
    set_by: staff
    rules: >-
      One per type, one ordinary preferred; seasonal month-day ranges cannot
      overlap or also be ordinary preferred.
    example: 'Home: 42 Harbor Avenue, Oakland, CA 94612, US'
    notes: Typed postal details including geographic and seasonal information.
  - name: mailing address
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: >-
      Select an eligible seasonal-preferred address, otherwise the ordinary
      preferred address.
    example: Winter residence during its selected season
    notes: The address applicable for mail on the workspace day.
  - name: communication preferences
    kind: collection
    required: false
    computed: false
    set_by: staff
    rules: >-
      One entry per communication type; enabled is required; subscription is
      subscribed, digest, unsubscribed or absent.
    example: 'Newsletter: enabled, digest, ruth@harbor.example'
    notes: Choices for each external communication category.
  - name: custom field values
    kind: collection
    required: false
    computed: false
    set_by: staff
    rules: Active Platform contact definitions govern value types.
    example: 'Professional designation: CAE'
    notes: Customer-defined extensions of the person’s information.
  - name: affiliations
    kind: collection
    required: false
    computed: false
    set_by: staff
    rules: >-
      Saved through the contact aggregate; the affiliation design explains each
      nested property.
    example: Current employer Harbor Engineering; current chapter Harbor West
    notes: The person’s dated organizational ties and responsibilities.
  - name: primary organization
    kind: computed
    required: false
    computed: true
    set_by: system
    rules: Derived on read; not a stored employer field.
    example: Harbor Engineering
    notes: The identity and label of the primary affiliation’s organization.
  - name: origin service
    kind: text
    required: false
    computed: false
    set_by: import
    rules: Import bookkeeping cannot be edited as an ordinary profile field.
    example: legacy-crm
    notes: The integration that supplies the imported origin.
  - name: origin record
    kind: text
    required: false
    computed: false
    set_by: import
    rules: Retained with origin service as import provenance.
    example: person-482
    notes: The source system’s reference for this person.
  - name: version
    kind: number
    required: true
    computed: true
    set_by: system
    rules: >-
      System advances it for a committed aggregate change; the caller echoes its
      read version.
    example: 7
    notes: The basis against which a later correction is compared.
  - name: lock
    kind: enum
    required: false
    computed: true
    set_by: system
    rules: >-
      Dedicated lock actions choose no lock, deletes, or updates & deletes;
      ordinary saves retain it.
    example: deletes
    notes: A customer restriction on mutation of this record.
  - name: created at
    kind: date
    required: true
    computed: true
    set_by: system
    rules: System timestamp; unchanged by later corrections.
    example: '2026-10-20T14:00:00Z'
    notes: When this identity is first stored.
  - name: created by
    kind: reference
    required: true
    computed: true
    set_by: system
    rules: System attribution; a read can resolve its current display name.
    example: Association coordinator
    notes: The actor responsible for creation.
  - name: modified at
    kind: date
    required: true
    computed: true
    set_by: system
    rules: A current-version no-op preserves this timestamp.
    example: '2026-10-23T15:30:00Z'
    notes: When the latest net change commits.
  - name: modified by
    kind: reference
    required: true
    computed: true
    set_by: system
    rules: >-
      Recorded with the write context; system work can invalidate an open
      editor.
    example: CRM system
    notes: The actor or system responsible for the latest change.
  - name: archived at
    kind: date
    required: false
    computed: true
    set_by: system
    rules: Only archive and unarchive actions control the timestamp.
    example: '2026-10-23T15:30:00Z'
    notes: Whether ordinary discovery and editing exclude this identity.
  - name: archived by
    kind: reference
    required: false
    computed: true
    set_by: system
    rules: Captured by the archive action and cleared on restoration.
    example: CRM administrator
    notes: Who sets this record aside.
  - name: archive reason
    kind: text
    required: false
    computed: true
    set_by: system
    rules: Optional trimmed reason limited to 500 characters.
    example: Firm closed
    notes: The explanation for setting the record aside.
  - name: unarchive reason
    kind: text
    required: false
    computed: true
    set_by: system
    rules: Optional trimmed reason limited to 500 characters.
    example: Firm reopened
    notes: The explanation for making the record available again.
  - name: contact addresses / disconnected_from_organization_id
    kind: date
    required: false
    computed: true
    set_by: system
    rules: Original organization retained after the live source is removed.
    example: Ohio Association Council
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / disconnected_at
    kind: date
    required: false
    computed: true
    set_by: system
    rules: Service-owned timestamp of the disconnection.
    example: '2026-10-23T14:00:00Z'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / disconnected_reason
    kind: text
    required: false
    computed: true
    set_by: system
    rules: Service-owned source-removal or organization-merge cause.
    example: organization_address_removed
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / address_type_id
    kind: text
    required: true
    computed: false
    set_by: staff
    rules: >-
      The workspace address purpose, subject to one-per-type and applicability
      validation.
    example: business
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / line1
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: First postal delivery line.
    example: 410 Association Way
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / line2
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Second postal delivery line.
    example: Suite 200
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / line3
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Additional postal delivery context.
    example: Member Services
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / city
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: City in the postal value.
    example: Columbus
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / state
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: State or province in the postal value.
    example: OH
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / postal_code
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Postal routing code.
    example: '43215'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / county
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: County supplied with the postal value.
    example: Franklin
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / country
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Country code; storage defaults to US.
    example: US
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / latitude
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Optional latitude supplied with the postal value.
    example: '39.96'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / longitude
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Optional longitude supplied with the postal value.
    example: '-83.00'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / time_zone_id
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Optional time-zone identity associated with the location.
    example: America/New_York
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / seasonal_start
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Recurring month-and-day beginning the seasonal period.
    example: 11-01
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / seasonal_end
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      Inclusive recurring month-and-day ending the season; the period can cross
      the year boundary.
    example: 03-31
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / seasonal_preferred
    kind: boolean
    required: false
    computed: false
    set_by: staff
    rules: >-
      The seasonal address is requested as the mailing destination while
      eligible and in season.
    example: 'true'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / is_preferred
    kind: boolean
    required: true
    computed: false
    set_by: staff
    rules: Preferred selection within this owner’s collection.
    example: 'true'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / source_mode
    kind: text
    required: true
    computed: false
    set_by: staff
    rules: >-
      Owned, copied, linked or disconnected explains the origin and
      live-resolution behavior.
    example: linked
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / source_affiliation_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: The contact’s affiliation through which a live source is supplied.
    example: Current employer relationship
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / source_organization_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: The organization supplying a live source through that affiliation.
    example: Ohio Association Council
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / source_organization_address_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: 'The supplying organization address, confined to the selected organization.'
    example: Headquarters address
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / copied_from_organization_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Informational organization identity retained with a one-time copy.
    example: Ohio Association Council
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact addresses / copied_at
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: The timestamp at which the organization values were copied.
    example: '2026-10-23T14:00:00Z'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - name: contact phone numbers / phone_number_type_id
    kind: text
    required: true
    computed: false
    set_by: staff
    rules: >-
      The workspace telephone purpose, subject to one-per-type and applicability
      validation.
    example: direct
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / phone_number
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: >-
      The telephone value; linked contact entries obtain their effective value
      from the organization source.
    example: +1 614 555 0100
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / is_preferred
    kind: boolean
    required: true
    computed: false
    set_by: staff
    rules: Preferred selection within this owner’s collection.
    example: 'true'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / source_mode
    kind: text
    required: true
    computed: false
    set_by: staff
    rules: >-
      Owned, copied, linked or disconnected explains the origin and
      live-resolution behavior.
    example: linked
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / source_affiliation_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: The contact’s affiliation through which a live source is supplied.
    example: Current employer relationship
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / source_organization_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: The organization supplying a live source through that affiliation.
    example: Ohio Association Council
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / source_organization_phone_number_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: >-
      The supplying organization telephone entry, confined to the selected
      organization.
    example: Main office phone
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / copied_from_organization_id
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Informational organization identity retained with a one-time copy.
    example: Ohio Association Council
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / copied_at
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: The timestamp at which the organization values were copied.
    example: '2026-10-23T14:00:00Z'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / disconnected_from_organization_id
    kind: date
    required: false
    computed: true
    set_by: system
    rules: Original organization retained after the live source is removed.
    example: Ohio Association Council
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / disconnected_at
    kind: date
    required: false
    computed: true
    set_by: system
    rules: Service-owned timestamp of the disconnection.
    example: '2026-10-23T14:00:00Z'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact phone numbers / disconnected_reason
    kind: text
    required: false
    computed: true
    set_by: system
    rules: Service-owned source-removal or organization-merge cause.
    example: organization_phone_number_removed
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - name: contact email addresses / position
    kind: text
    required: true
    computed: true
    set_by: system
    rules: Service-owned email slot from one through three; slot one is primary.
    example: '1'
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-email-addresses.table.ts'
  - name: contact email addresses / email_address
    kind: text
    required: true
    computed: false
    set_by: staff
    rules: The required email value in an ordered contact email entry.
    example: ruth@association.example
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-email-addresses.table.ts'
  - name: contact communication preferences / communication_type_id
    kind: date
    required: true
    computed: false
    set_by: staff
    rules: Communication classification identity within the preference collection.
    example: legislative-updates
    provenance: observed
    source: >-
      services-crm:modules/entities/db/contact-communication-preferences.table.ts
  - name: contact communication preferences / enabled
    kind: boolean
    required: true
    computed: false
    set_by: staff
    rules: 'The saved preference’s enabled choice, separate from global email opt-out.'
    example: 'true'
    provenance: observed
    source: >-
      services-crm:modules/entities/db/contact-communication-preferences.table.ts
  - name: contact communication preferences / subscription_email_address
    kind: text
    required: false
    computed: false
    set_by: staff
    rules: Optional email value for this subscription.
    example: ruth@association.example
    provenance: observed
    source: >-
      services-crm:modules/entities/db/contact-communication-preferences.table.ts
  - name: contact communication preferences / subscription_status
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: 'Optional subscribed, digest or unsubscribed value.'
    example: digest
    provenance: observed
    source: >-
      services-crm:modules/entities/db/contact-communication-preferences.table.ts
  - name: contact communication preferences / subscription_last_updated
    kind: date
    required: false
    computed: false
    set_by: staff
    rules: Optional timestamp associated with subscription state.
    example: '2026-10-23T14:00:00Z'
    provenance: observed
    source: >-
      services-crm:modules/entities/db/contact-communication-preferences.table.ts
relationships:
  - to: DOM-CRM-ENT-003
    cardinality: one-to-many
    ownership: >-
      The contact owns these relationship periods and persists them as part of
      its save.
    cross_service: false
    on_archive_or_delete: >-
      Archive keeps the affiliation rows; deleting the contact cascades
      affiliations and roles.
  - to: DOM-CRM-ENT-002
    cardinality: >-
      many-to-many, through affiliations; a linked address or phone number also
      names one organization detail
    ownership: >-
      The organization owns the shared detail; this contact owns the reference
      to it.
    cross_service: false
    on_archive_or_delete: >-
      Archive keeps source references; organization deletion is blocked by
      affiliations, and removing a source disconnects linked details.
  - to: DOM-CRM-ENT-001
    cardinality: 'one-to-one (spouse), reciprocal'
    ownership: >-
      Each spouse stores a reference to the other, maintained together by the
      service.
    cross_service: false
    on_archive_or_delete: >-
      Archive preserves the pair; deleting either contact clears the surviving
      spouse reference.
  - to: DOM-CRM-ENT-033
    cardinality: 'one-to-one safeguards per contact, one-to-many revisions and lock changes'
    ownership: The safeguard model defines protection and history for the contact.
    cross_service: false
    on_archive_or_delete: >-
      Archive retains current safeguards; ordinary deletion retains revision
      evidence.
invariants:
  - id: INV-DOM-CRM-ENT-001-01
    statement: >-
      First name or last name must contain text; another name part cannot
      substitute for both.
    enforced_at:
      - api
      - service
      - database
      - console
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contacts-names.e2e.spec.js#answers
      400 for a contact with neither a first nor a last name, naming the rule
    protects: Recognizable identity when staff create or find a person.
  - id: INV-DOM-CRM-ENT-001-02
    statement: >-
      Changes to name parts recalculate display, full and sort names;
      client-provided derived names do not control the result.
    enforced_at:
      - api
      - service
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/contacts.ts:contactNames
    protects: 'Agreement between record headers, correspondence names and sorting.'
  - id: INV-DOM-CRM-ENT-001-03
    statement: >-
      The service assigns a workspace-unique contact number from its counter and
      does not recycle allocated numbers.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/contacts.ts:insertContact
    protects: Stable human references across unsuccessful and later successful creates.
  - id: INV-DOM-CRM-ENT-001-04
    statement: 'Contact status accepts only active, inactive, deceased or duplicate.'
    enforced_at:
      - database
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/db/contacts.table.integration.spec.js#refuses
      a status outside the closed list by contacts_status_check
    protects: Consistent interpretation of status by selection and downstream processes.
  - id: INV-DOM-CRM-ENT-001-05
    statement: >-
      Within a workspace, only one active or duplicate contact may reserve a
      given primary email address. Inactive and deceased contacts release that
      reservation.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/db/contact-email-addresses.table.integration.spec.js#refuses
      to make an inactive contact active while another contact holds its primary
      address
    protects: Unambiguous primary-email ownership among reserving contacts.
  - id: INV-DOM-CRM-ENT-001-06
    statement: >-
      The email list contains at most three ordered entries, with the first
      designated primary.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      a fourth e-mail address
    protects: >-
      A bounded, ordered destination list rather than competing primary
      pointers.
  - id: INV-DOM-CRM-ENT-001-07
    statement: >-
      Address and phone types each permit one entry per contact and one
      preferred entry per collection. A seasonal address cannot also be
      preferred.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contacts.integration.spec.js#refuses
      two numbers of the same type
    protects: Deterministic contact-point selection within each purpose and collection.
  - id: INV-DOM-CRM-ENT-001-08
    statement: A contact cannot have overlapping seasonal address periods.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-crm:modules/entities/tests/unit/services/contacts/seasonal-addresses.spec.js#refuses
      overlapping seasons naming both addresses
    protects: A single seasonal destination for any business day.
  - id: INV-DOM-CRM-ENT-001-09
    statement: >-
      Spouse references stay reciprocal and workspace-local; the prospective
      spouse cannot already belong to another spouse pair.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-crm:modules/entities/services/persistence/contacts.ts:keepSpouseReciprocal
    protects: A coherent two-person connection without foreign workspace references.
  - id: INV-DOM-CRM-ENT-001-10
    statement: >-
      Shared contact-point values must match the organization source after
      whitespace normalization.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contact-information-sources.integration.spec.js#refuses
      a value that differs from the organization’s in letter case, naming the
      field (A19)
    protects: A live shared value that cannot quietly differ from its stated source.
  - id: INV-DOM-CRM-ENT-001-11
    statement: >-
      Disconnection metadata is service-owned and records the original
      organization, time and cause.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contacts-disconnected.e2e.spec.js#refuses
      a client that asks for a row to be disconnected, naming the field
    protects: >-
      Trustworthy explanation of a removed source, distinct from user-entered
      copies.
  - id: INV-DOM-CRM-ENT-001-12
    statement: >-
      Custom values must use an active workspace contact definition and satisfy
      its declared type.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contacts-custom-fields.e2e.spec.js#refuses
      a write to an archived field
    protects: >-
      Meaningful customer extensions rather than values with missing or
      incompatible definitions.
  - id: INV-DOM-CRM-ENT-001-13
    statement: >-
      An organizations-only address or phone type cannot be newly assigned to a
      contact; an existing assignment may be retained.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-crm:modules/entities/tests/integration/services/persistence/contacts.field-rules.integration.spec.js#leaves
      a contact saveable after the type of an address it holds became
      organizations-only
    protects: >-
      New choices that respect applicability without making legacy records
      uneditable.
  - id: INV-DOM-CRM-ENT-001-14
    statement: >-
      The referenced contact type must belong to the workspace; catalogue
      inactivity does not invalidate the reference.
    enforced_at:
      - api
      - database
    provenance: test
    source: >-
      services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 409
      for a type the tenant does not have, and saves no contact
    protects: Workspace isolation while preserving existing classification meaning.
physical:
  - contacts
  - contact_email_addresses
  - contact_phone_numbers
  - contact_addresses
  - contact_communication_preferences
  - contact_custom_field_values
  - contact_types
  - address_types
  - phone_number_types
  - managed_assets
  - contact_change_events
  - contacts_for_queries
  - contact_addresses_effective
  - contact_phone_numbers_effective
evidence:
  - >-
    services-crm at 7ea48a41: modules/entities/db/contacts.table.ts,
    contact-email-addresses.table.ts, contact-phone-numbers.table.ts,
    contact-addresses.table.ts, contact-communication-preferences.table.ts,
    contact-custom-field-values.table.ts, contact-types.table.ts,
    collection-types.ts; services/persistence/contacts.ts (createContact,
    updateContact, deleteContact, insertContact, keepSpouseReciprocal);
    services/contacts/seasonal-addresses.ts; metadata/contacts.object.ts
    (capabilities); docs/entities-api.md; docs/disconnected-information.md.
  - >-
    services-platform at ad3f6a65: docs/standards/custom-field-capability.md §2
    (CRM contacts carry custom fields).
  - >-
    react-console at da630758: components/contact-editor/contact-editor.tsx;
    lib/contacts/crm-live.ts.
permissions:
  - action: read
    permission: crm.contacts.read
    roles: [Administrator, Power User, Standard User, Read Only]
  - action: create
    permission: crm.contacts.create
    roles: [Administrator, Power User, Standard User]
  - action: update
    permission: crm.contacts.update
    roles: [Administrator, Power User, Standard User]
    by_state: >-
      Ordinary update refuses archived or fully locked records; archive actions
      also check full locks.
  - action: archive
    permission: crm.contacts.archive
    roles: [Administrator, Power User]
    by_state: >-
      Ordinary update refuses archived or fully locked records; archive actions
      also check full locks.
  - action: delete
    permission: crm.contacts.delete
    roles: [Administrator, Power User]
  - action: lock
    permission: crm.contacts.lock
    roles: [Administrator, Power User]
  - action: unlock
    permission: crm.contacts.unlock
    roles: [Administrator, Power User]
  - action: history
    permission: crm.contacts.history
    roles: [Administrator, Power User, Standard User, Read Only]
narrative:
  status: verified
  review: NARRATIVE-REVIEW-2026-10-23
narrative_sources:
  - section: What It Models
    provenance: observed
    source: 'services-crm:modules/entities/services/persistence/contacts.ts'
  - section: Identity and Attributes
    provenance: observed
    source: 'services-crm:modules/entities/services/persistence/contacts.ts'
  - section: Relationships
    provenance: observed
    source: 'services-crm:modules/entities/services/persistence/contacts.ts'
  - section: Lifecycle
    provenance: observed
    source: 'services-crm:modules/entities/services/persistence/contacts.ts'
  - section: How It Is Stored and Shared
    provenance: observed
    source: 'services-crm:modules/entities/services/persistence/contacts.ts'
  - section: Permissions
    provenance: documented
    source: 'rhythm-v2-docs:specs/crm/AUTHORIZATION.md#Permission Catalogue'
  - section: Invariants
    provenance: inferred
    source: 'services-crm:modules/entities/services/persistence/contacts.ts'
    note: >-
      Protection explanations interpret the existing invariant rows and
      enforcement boundaries; no new product decision is claimed.
  - section: What It Models
    provenance: inferred
    source: 'services-crm:modules/entities/services/persistence/contacts.ts'
    note: >-
      The explicitly labeled aggregate rationale follows the object boundary and
      its owned records.
  - section: Open Design Questions
    provenance: documented
    source: 'rhythm-v2-docs:specs/crm/entities/FINDINGS.md#Findings'
  - section: Identity and Attributes
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-addresses.table.ts'
  - section: Identity and Attributes
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-phone-numbers.table.ts'
  - section: Identity and Attributes
    provenance: observed
    source: 'services-crm:modules/entities/db/contact-email-addresses.table.ts'
  - section: Identity and Attributes
    provenance: observed
    source: >-
      services-crm:modules/entities/db/contact-communication-preferences.table.ts
  - section: Invariants
    provenance: inferred
    source: "services-crm:modules/entities/services/persistence/contacts.ts; services-crm:modules/entities/services/persistence/organizations.ts; services-crm:modules/entities/services/organizations/compliance/organization-compliance.ts; services-crm:modules/entities/services/persistence/record-locks.ts"
    note: "The rationale interprets this artifact’s sourced invariants and linked capability rules; it does not claim a recorded product decision."
---

# Contact

## What It Models

A contact models one person who can remain involved with an association across employers, chapters and responsibilities. The person’s identity joins personal information, communication choices and dated ties in one save boundary. For example, Ruth Kim can change firms while keeping her chapter affiliation and the same contact number.

Our reading is that this aggregate prevents a change in employment or address from looking like a different person. The contact owns relationship periods and contact points, while Platform owns their field definitions and presentation. A classification named Member supplies a CRM category; it does not establish membership entitlement.

## Identity and Attributes

Contacts have workspace-scoped identities. Creation keeps a valid unused client identity or generates one. The service allocates a separate staff-facing contact number; clients cannot choose it, and allocated numbers are not recycled.

The table describes logical properties. Optional values can be absent; a required collection member is distinct from requiring the collection itself. Examples illustrate association information and do not establish new defaults. Service-owned values remain separate from fields that staff or integrations supply.

| Property | Meaning and writer | Constraints | Example |
|---|---|---|---|
| id | A continuing identity for integrations and record references. Set by integration. | Unused workspace identity with letters, digits, underscores, hyphens or vertical bars; generated when omitted. | ruth_kim |
| number | The staff-facing contact reference. Set by system. | Assigned by the workspace contact counter; never chosen or recycled by a client. | 10428 |
| status | The person’s current business classification of availability. Set by staff. | Active is the default; active, inactive, deceased and duplicate are the closed choices. | active |
| contact type | The workspace category that selects effective forms and layouts. Set by staff. | Optional workspace contact type; retained inactive references remain valid. | Member |
| prefix | Title appearing before the full name. Set by staff. | Trimmed optional text up to 200 characters; first or last must contain text. | Dr. |
| salutation | How correspondence addresses the person. Set by staff. | Trimmed optional text; cannot replace both first and last names. | Dear Ruth |
| first name | Given name used in display. Set by staff. | Trimmed optional text up to 200 characters; first or last must contain text. | Ruth |
| middle name | Middle name reduced to an initial in derived names. Set by staff. | Trimmed optional text up to 200 characters; first or last must contain text. | Anne |
| last name | Family name used for display and sorting. Set by staff. | Trimmed optional text up to 200 characters; first or last must contain text. | Kim |
| maiden name | Earlier family name retained as personal information. Set by staff. | Trimmed optional text; cannot replace both first and last names. | Park |
| nickname | Familiar name included in derived presentation. Set by staff. | Trimmed optional text up to 200 characters; first or last must contain text. | Roo |
| suffix | Name suffix. Set by staff. | Trimmed optional text up to 200 characters; first or last must contain text. | Jr. |
| credentials | Professional qualifications following the display name. Set by staff. | Trimmed optional text; cannot replace both first and last names. | CAE |
| display name | The compact name used in ordinary record presentation. Set by system. | Derived from editable name parts. Creation refuses direct derived-name input; maintenance permits unchanged read-back values, while attempts to change them independently are refused. | Ruth A. Kim, CAE |
| full name | The display name with its prefix. Set by system. | Computed alongside the other derived names. | Dr. Ruth A. Kim, CAE |
| sort name | The family-first label for ordering people. Set by system. | Computed from the same supplied name components. | Kim, Ruth A. |
| job title | The title associated with the prominent organizational connection. Set by staff. | Trimmed optional text; saved contact and primary-affiliation titles are coordinated. | Executive Director |
| spouse | An interim reciprocal personal connection. Set by staff. | Another contact in this workspace; cannot be self or already paired to another person. | Daniel Kim |
| photo | The person’s image for record presentation. Set by staff. | Platform file reference made permanent before persistence; display links are returned on read. | Ruth’s portrait |
| gender | Optional stored gender information. Set by staff. | The observed closed choices are male and female; broader product vocabulary is not established. | female |
| pronouns | The person’s stated pronouns. Set by staff. | Optional trimmed text. | she/her |
| date of birth | The person’s birth date when known. Set by staff. | Optional ISO calendar date, without a time. | 1984-04-12 |
| date of death | The recorded death date when known. Set by staff. | Optional ISO calendar date; separate from selecting deceased status. | 2026-03-01 |
| notes | Free text that staff retain about the person. Set by staff. | Trimmed text up to 10,000 characters. | Prefers calls after the monthly board meeting. |
| Facebook | The recorded social-profile reference. Set by staff. | Optional trimmed text; no verified guarantee that the remote profile exists. | https://www.facebook.com/ruth.kim |
| Twitter | The recorded social-profile reference. Set by staff. | Optional trimmed text; no verified guarantee that the remote profile exists. | @RuthKim |
| Instagram | The recorded social-profile reference. Set by staff. | Optional trimmed text; no verified guarantee that the remote profile exists. | @ruthkim |
| LinkedIn | The recorded social-profile reference. Set by staff. | Optional trimmed text; no verified guarantee that the remote profile exists. | https://www.linkedin.com/in/ruth-kim |
| profile last updated | The stored time of profile review. Set by staff. | Optional ISO timestamp accepted by the record schema. | 2026-10-20T14:00:00Z |
| social profile last updated | The stored time of social-profile review. Set by staff. | Optional ISO timestamp. | 2026-10-20T14:00:00Z |
| region | The regional grouping selected from the deciding address. Set by system. | Regions assigns it unless automatic assignment is overridden; reference stays workspace-local. | Western Region |
| override automatic region assignment | Whether an explicit region choice takes precedence. Set by staff. | Optional boolean accepted on the person record. | true |
| national district | The label or reference supplied by district lookup. Set by system. | Metadata presents this as read-only; API enforcement is an existing unresolved finding. | CA-12 |
| national district OCD reference | The label or reference supplied by district lookup. Set by system. | Metadata presents this as read-only; API enforcement is an existing unresolved finding. | ocd-division/country:us/state:ca/cd:12 |
| state upper district | The label or reference supplied by district lookup. Set by system. | Metadata presents this as read-only; API enforcement is an existing unresolved finding. | California Senate District 7 |
| state upper district OCD reference | The label or reference supplied by district lookup. Set by system. | Metadata presents this as read-only; API enforcement is an existing unresolved finding. | ocd-division/country:us/state:ca/sldu:7 |
| state lower district | The label or reference supplied by district lookup. Set by system. | Metadata presents this as read-only; API enforcement is an existing unresolved finding. | California Assembly District 18 |
| state lower district OCD reference | The label or reference supplied by district lookup. Set by system. | Metadata presents this as read-only; API enforcement is an existing unresolved finding. | ocd-division/country:us/state:ca/sldl:18 |
| do not mail | A broad postal restriction recorded for the person. Set by staff. | Boolean defaults to false; storage does not prove every downstream sender honors it. | true |
| do not call | A broad telephone restriction recorded for the person. Set by staff. | Boolean defaults to false; storage does not prove every downstream sender honors it. | false |
| email opt-out | A broad email restriction recorded for the person. Set by staff. | Boolean defaults to false; storage does not prove every downstream sender honors it. | true |
| communication preferences last confirmed | When the person’s choices are last reviewed. Set by staff. | Optional ISO timestamp. | 2026-10-22T13:00:00Z |
| email addresses | Ordered destinations, with the first used as primary. Set by staff. | At most three canonical lowercase addresses, each up to 254 characters; primary reservation depends on status. | ["ruth@harbor.example","ruth.kim@example.org"] |
| phone numbers | Typed numbers with optional extension, preference and source. Set by staff. | One per type, one preferred at most; stored source distinguishes owned, copied, linked and disconnected values. | Work: +1 510 555 0134, extension 204 |
| addresses | Typed postal details including geographic and seasonal information. Set by staff. | One per type, one ordinary preferred; seasonal month-day ranges cannot overlap or also be ordinary preferred. | Home: 42 Harbor Avenue, Oakland, CA 94612, US |
| mailing address | The address applicable for mail on the workspace day. Set by system. | Select an eligible seasonal-preferred address, otherwise the ordinary preferred address. | Winter residence during its selected season |
| communication preferences | Choices for each external communication category. Set by staff. | One entry per communication type; enabled is required; subscription is subscribed, digest, unsubscribed or absent. | Newsletter: enabled, digest, ruth@harbor.example |
| custom field values | Customer-defined extensions of the person’s information. Set by staff. | Active Platform contact definitions govern value types. | Professional designation: CAE |
| affiliations | The person’s dated organizational ties and responsibilities. Set by staff. | Saved through the contact aggregate; the affiliation design explains each nested property. | Current employer Harbor Engineering; current chapter Harbor West |
| primary organization | The identity and label of the primary affiliation’s organization. Set by system. | Derived on read; not a stored employer field. | Harbor Engineering |
| origin service | The integration that supplies the imported origin. Set by import. | Import bookkeeping cannot be edited as an ordinary profile field. | legacy-crm |
| origin record | The source system’s reference for this person. Set by import. | Retained with origin service as import provenance. | person-482 |
| version | The basis against which a later correction is compared. Set by system. | System advances it for a committed aggregate change; the caller echoes its read version. | 7 |
| lock | A customer restriction on mutation of this record. Set by system. | Dedicated lock actions choose no lock, deletes, or updates & deletes; ordinary saves retain it. | deletes |
| created at | When this identity is first stored. Set by system. | System timestamp; unchanged by later corrections. | 2026-10-20T14:00:00Z |
| created by | The actor responsible for creation. Set by system. | System attribution; a read can resolve its current display name. | Association coordinator |
| modified at | When the latest net change commits. Set by system. | A current-version no-op preserves this timestamp. | 2026-10-23T15:30:00Z |
| modified by | The actor or system responsible for the latest change. Set by system. | Recorded with the write context; system work can invalidate an open editor. | CRM system |
| archived at | Whether ordinary discovery and editing exclude this identity. Set by system. | Only archive and unarchive actions control the timestamp. | 2026-10-23T15:30:00Z |
| archived by | Who sets this record aside. Set by system. | Captured by the archive action and cleared on restoration. | CRM administrator |
| archive reason | The explanation for setting the record aside. Set by system. | Optional trimmed reason limited to 500 characters. | Firm closed |
| unarchive reason | The explanation for making the record available again. Set by system. | Optional trimmed reason limited to 500 characters. | Firm reopened |

### Detail collection properties

Each entry belongs to its aggregate and has its own identity and system lock/stamps. These child properties explain the exposed value and source fields without creating independent domain objects. Organization values supply linked contact values only through the verified affiliation and source references.

| Property | Meaning | Example |
|---|---|---|
| contact addresses / disconnected_from_organization_id | Original organization retained after the live source is removed. | Ohio Association Council |
| contact addresses / disconnected_at | Service-owned timestamp of the disconnection. | 2026-10-23T14:00:00Z |
| contact addresses / disconnected_reason | Service-owned source-removal or organization-merge cause. | organization_address_removed |
| contact addresses / address_type_id | The workspace address purpose, subject to one-per-type and applicability validation. | business |
| contact addresses / line1 | First postal delivery line. | 410 Association Way |
| contact addresses / line2 | Second postal delivery line. | Suite 200 |
| contact addresses / line3 | Additional postal delivery context. | Member Services |
| contact addresses / city | City in the postal value. | Columbus |
| contact addresses / state | State or province in the postal value. | OH |
| contact addresses / postal_code | Postal routing code. | 43215 |
| contact addresses / county | County supplied with the postal value. | Franklin |
| contact addresses / country | Country code; storage defaults to US. | US |
| contact addresses / latitude | Optional latitude supplied with the postal value. | 39.96 |
| contact addresses / longitude | Optional longitude supplied with the postal value. | -83.00 |
| contact addresses / time_zone_id | Optional time-zone identity associated with the location. | America/New_York |
| contact addresses / seasonal_start | Recurring month-and-day beginning the seasonal period. | 11-01 |
| contact addresses / seasonal_end | Inclusive recurring month-and-day ending the season; the period can cross the year boundary. | 03-31 |
| contact addresses / seasonal_preferred | The seasonal address is requested as the mailing destination while eligible and in season. | true |
| contact addresses / is_preferred | Preferred selection within this owner’s collection. | true |
| contact addresses / source_mode | Owned, copied, linked or disconnected explains the origin and live-resolution behavior. | linked |
| contact addresses / source_affiliation_id | The contact’s affiliation through which a live source is supplied. | Current employer relationship |
| contact addresses / source_organization_id | The organization supplying a live source through that affiliation. | Ohio Association Council |
| contact addresses / source_organization_address_id | The supplying organization address, confined to the selected organization. | Headquarters address |
| contact addresses / copied_from_organization_id | Informational organization identity retained with a one-time copy. | Ohio Association Council |
| contact addresses / copied_at | The timestamp at which the organization values were copied. | 2026-10-23T14:00:00Z |
| contact phone numbers / phone_number_type_id | The workspace telephone purpose, subject to one-per-type and applicability validation. | direct |
| contact phone numbers / phone_number | The telephone value; linked contact entries obtain their effective value from the organization source. | +1 614 555 0100 |
| contact phone numbers / is_preferred | Preferred selection within this owner’s collection. | true |
| contact phone numbers / source_mode | Owned, copied, linked or disconnected explains the origin and live-resolution behavior. | linked |
| contact phone numbers / source_affiliation_id | The contact’s affiliation through which a live source is supplied. | Current employer relationship |
| contact phone numbers / source_organization_id | The organization supplying a live source through that affiliation. | Ohio Association Council |
| contact phone numbers / source_organization_phone_number_id | The supplying organization telephone entry, confined to the selected organization. | Main office phone |
| contact phone numbers / copied_from_organization_id | Informational organization identity retained with a one-time copy. | Ohio Association Council |
| contact phone numbers / copied_at | The timestamp at which the organization values were copied. | 2026-10-23T14:00:00Z |
| contact phone numbers / disconnected_from_organization_id | Original organization retained after the live source is removed. | Ohio Association Council |
| contact phone numbers / disconnected_at | Service-owned timestamp of the disconnection. | 2026-10-23T14:00:00Z |
| contact phone numbers / disconnected_reason | Service-owned phone-source removal or organization-merge cause. | organization_phone_number_removed |
| contact email addresses / position | Service-owned email slot from one through three; slot one is primary. | 1 |
| contact email addresses / email_address | The required email value in an ordered contact email entry. | ruth@association.example |
| contact communication preferences / communication_type_id | Communication classification identity within the preference collection. | legislative-updates |
| contact communication preferences / enabled | The saved preference’s enabled choice, separate from global email opt-out. | true |
| contact communication preferences / subscription_email_address | Optional email value for this subscription. | ruth@association.example |
| contact communication preferences / subscription_status | Optional subscribed, digest or unsubscribed value. | digest |
| contact communication preferences / subscription_last_updated | Optional timestamp associated with subscription state. | 2026-10-23T14:00:00Z |

## Relationships

A person owns any number of affiliations, and each affiliation names one organization. Multiple ties can connect that person to the same body when their kinds or periods differ. Deleting the person removes these live children; archiving preserves them. An organization cannot be deleted while any affiliation still names it, even one that has ended.

Addresses and phones can reference an organization-owned source through a tie. Removing that source preserves its last value as disconnected information on the contact. The spouse reference is at most one reciprocal pair: deleting one person clears the survivor’s link with system attribution. Versions, archive metadata, locks and history belong to the person’s safeguards; retained revisions survive ordinary deletion.

## Lifecycle

The contact record lifecycle distinguishes active, inactive, deceased and duplicate status from the separate archive flag. Ruth can be inactive and also archived; restoring her availability does not make her active automatically. The lifecycle model explains the accepted transitions and the status-sensitive email and automatic-role effects.

Hard deletion removes the live aggregate, while history retains its explanation. There is no restore operation for a deleted contact. The allocated number remains spent. A direct read of an archived identity can still succeed, even though ordinary selection and correction are restricted.

## Invariants

A person’s continuing identity separates correction from creation. Changing a name or telephone number must leave affiliations, spouse context and revision inspection attached to the same person. Workspace-local references keep a valid-looking classification or relationship from pointing into another customer’s records. A duplicate refusal is therefore a prompt to inspect identity, rather than permission to reuse another person’s associations.

Contact replacement and nested relationship changes belong to the contact transaction. The current version protects a coordinator from overwriting another editor’s intervening work, while collection validation prevents a partial save from leaving an unusable primary or shared detail. Computed name and primary-organization context must agree with the editable source values. Derived values returned by a read are not independent instructions to rename the person.

Communication details preserve distinctions that affect interpretation: e-mail order supplies primary identity, phone and address purposes support preferred selections, and seasonal address choice is separate from the ordinary preference flag. A retained disconnected value records source loss; it is not evidence that staff verified a new independently maintained value. Reads and revisions let staff distinguish those states without discarding the person’s continuing context.

## Permissions

The CRM authorization model separates workspace access from permission for a particular action.

The service checks these grants at the corresponding operation boundary. Additional record guards still apply: grants do not bypass archive, customer restrictions, dependencies, version comparison or Rhythm protection. Read access and history access are separate; record history checks the applicable read authority as well as the history grant.

## How It Is Stored and Shared

Entities persists the person row and its owned collections together, including contact custom values, affiliation periods and responsibilities. Effective address and phone reads resolve organization links instead of copying every organization change into every person. Derived names and primary-organization context shape the returned record.

Platform supplies metadata, access evaluation, actor display names and file handling. Contact events carry committed changes to search, region and other CRM consumers after the record transaction. Those consumers can lag or fail without undoing the person’s committed revision; a saved record and a refreshed projection are separate outcomes.

## Open Design Questions

API enforcement of read-only civic districts is not established. Communication-type identity and downstream honoring of restrictions also remain open evidence boundaries. The existing findings cover those gaps and the missing dedicated refresh-recovery proof. The implementation of a field or callback does not confirm its product intent.
