Last deployment:

Turfi Platform Documentation

Official Turfi documentation portal for users, admins, and developers.

Back to support

Documentation Search

Search only within Turfi documentation pages.

C

Contacts

Shared contact registry contract.

Entity: Contacts

1. Overview

  • Shared normalized contact registry for reusable person/contact records linked across entities and facilities.
  • Acts as a reusable shared-data entity.
  • Can be linked through centralized entity-contact relationships and facility forms.
  • Visible at /admin/data/registries/contacts.

2. Database Schema

Columns

columntypenullabledefaultnotes
created_atstringnodatabase default or generatedBase table field.
emailstring \nullyesnullBase table field.
first_namestring \nullyesnullBase table field.
full_namestring \nullyesnullBase table field.
idstringnogenerated UUID defaultBase table field.
last_namestring \nullyesnullBase table field.
normalized_namestring \nullyesnullBase table field.
notesstring \nullyesnullBase table field.
phonestring \nullyesnullBase table field.
search_namestring \nullyesnullBase table field.
statusstringnodatabase default or generatedLifecycle field when present.
titlestring \nullyesnullBase table field.
updated_atstringnodatabase default or generatedBase table field.
websitestring \nullyesnullBase table field.

Foreign Keys

No foreign keys documented by the generated schema contract.

Indexes

No migration-defined indexes were discovered in the checked-in SQL history for this table.

Constraints

  • Unique: none discovered in checked-in migrations.
  • Check: none discovered in checked-in migrations.

Triggers

No migration-defined triggers were discovered in the checked-in SQL history for this table.

Views (if any)

namepurpose
api_contactsShared-data read model for contact registry operations.

3. Import Contract

Accepted Columns

  • key
  • slug
  • full_name
  • first_name
  • last_name
  • label
  • contact_name
  • email
  • phone
  • website
  • notes
  • status

Resolution Rules

  • Direct contact imports normalize names from first_name, last_name, full_name, contact_name, or fallback name.
  • Contact matching checks direct id first, then exact combinations of full_name, email, and phone.
  • If exactly one existing contact matches, the importer updates that contact; if none match, it creates a new one; if multiple match, the row fails as ambiguous.

Required Fields

  • first_name

Optional Fields

  • key
  • slug
  • full_name
  • last_name
  • label
  • contact_name
  • email
  • phone
  • website
  • notes
  • status

Failure Rules

  • Missing first_name fails immediately.
  • Invalid status or malformed contact fields fail validation rather than relationship mismatch handling.

4. Frontend Registry Mapping

Grid Columns

labelfieldsource
First Namefirst_namederived structured name
Last Namelast_namederived structured name
Titletitlecontacts.label
Emailemailnormalized contacts.email
Phonephonenormalized contacts.phone
Statusstatuscontacts.status

Filters

filterfieldtype
Searchfull_name, first_name, last_name, title, email, phonetext substring
First Namefirst_nametext
Last Namelast_nametext
Emailemailtext
Titlelabeltext
Statusstatusselect

Display Logic

  • Structured names are derived from first/last name where available, otherwise split from full-name style fields.
  • The displayed Title column is backed by the label field.
  • Phone values are normalized and formatted in-grid.

5. Lifecycle Rules

  • Shared contacts should be inactivated or relinked rather than hard-deleted when downstream entities still reference them.
  • Contact lifecycle should preserve communication history and relationship summaries.

Relationships

  • There is no direct contact_id column on the core registry tables documented here.
  • entity_contacts.contact_id -> contacts.id is the reusable linking table for shared contacts.
  • entity_contacts.contact_role_id -> contact_roles.id assigns an optional semantic role to each link.
  • entity_contacts supports club, organization, venue, business, and user entity types.
  • The model is many-to-many: one contact can be linked to many entities, and one entity can have many contacts.
  • Primary-link behavior is enforced by idx_entity_contacts_primary_unique plus trg_entity_contacts_single_primary.
  • When the final entity_contacts link is removed, trg_entity_contacts_cleanup_orphan deletes the orphaned contact record automatically.

Import Contract Detail

  • Contacts are imported as first-class shared records through the contacts import adapter.
  • Current organization/league/club import adapters do not create inline shared contact rows during CSV import the way they do for inline addresses.
  • Inline contact creation and linking is supported in admin entity editors that save reusable contact bundles through saveEntityContactAddressBundle().
  • Required import field is first_name; the adapter also accepts full_name, last_name, label, contact_name, email, phone, website, notes, and status.
  • When an existing contact is found by exact full_name / email / phone matching, the record is updated instead of duplicated.

Views / Joins

  • api_contacts reads directly from contacts and adds linked_entity_count plus primary_entity_count from entity_contacts.
  • The top-level contacts registry shows one row per contact, not one row per entity-contact link.
  • Structured display names are derived in the UI from first_name and last_name, with fallback to full_name or contact_name.
  • When bundle-linked contacts are loaded for an entity, the service orders primary contacts first and then additional contacts by display_order ascending.
  • Current organization, league, and club grids do not render multi-contact lists inline; contact editing there is either direct summary-field editing or handled in dedicated bundle editors for entities that support them.

Usage Across Entities

  • Current shared-link consumers in entity_contacts: clubs, organizations, venues, businesses, and users.
  • Summary phone propagation is implemented for organizations, businesses, and users through trg_contacts_refresh_dependents and refresh_entity_primary_contact_summary().
  • Organizations, leagues, and clubs also store direct summary fields such as phone, email, and website, but those are not modeled as direct contact FKs.
  • Because contacts are shared, updating one contact row affects every entity linked to that same contact record.
  • Display priority for linked contacts is: primary contact first, then additional contacts ordered by display_order.