Contact
Data Entity
Description
Represents an individual who is served by or associated with a peer mentor programme — a service recipient, family member, or professional referral. Contact records include profile fields, notes, and assignment history. Some fields (address, medical context) are encrypted at rest and require screen reader disclosure warnings before vocalization.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Primary key, generated server-side as a UUID v4 | PKrequiredunique |
organization_id |
uuid |
Foreign key to the organization that owns this contact record. Enforces multi-tenant data isolation via Supabase RLS. All queries must be scoped to this value. | required |
first_name |
string |
Contact's given name. Stored in plain text. Used for display, search, and screen reader announcements. | required |
last_name |
string |
Contact's family name. Stored in plain text. Combined with first_name for full-name search and display. | required |
email |
string |
Contact's email address. Optional. Must pass RFC 5322 format validation when provided. Not unique globally; uniqueness is scoped per organization. | - |
phone |
string |
Contact's phone number in Norwegian or international E.164 format. Optional. Used for coordinator lookup and assignment matching. | - |
address_encrypted |
text |
Contact's physical address, encrypted at rest using organization-scoped keys. Must not be vocalized by screen readers without first triggering the sensitive field disclosure warning dialog. Decrypted on demand by field-encryption-utils. | - |
medical_context_encrypted |
text |
Medical background or epikrise (clinical summary) relevant to the peer mentoring relationship, e.g., diagnosis or functional limitations. Encrypted at rest. Requires disclosure warning before vocalization. Used primarily by Blindeforbundet for home-visit context. | - |
notes |
text |
Free-text notes about the contact written by coordinators or peer mentors. Indexed for full-text search within the organization. Not encrypted but access-controlled via RLS. | - |
contact_type |
enum |
Categorizes the nature of the contact's relationship to the programme. Determines how the contact is filtered in role-specific list views. | required |
status |
enum |
Lifecycle status of the contact record. Archived contacts are hidden from default list views but retained for reporting. Inactive contacts are not assignable. | required |
date_of_birth |
datetime |
Contact's date of birth. Optional. Used for demographic reporting and eligibility checks. Must not be a future date. | - |
gender |
string |
Self-reported gender identity. Optional, free-form to accommodate all identities. Used for Bufdir demographic reporting where required. | - |
preferred_contact_method |
enum |
Preferred communication channel for reaching this contact. Informs coordinators and peer mentors before initiating outreach. | - |
is_deleted |
boolean |
Soft-delete flag. When true, the contact is excluded from all standard queries and UI views. Hard deletion is never performed to preserve audit and reporting integrity. | required |
deleted_at |
datetime |
Timestamp when is_deleted was set to true. Null for non-deleted records. Used for audit trails and compliance reporting. | - |
created_by |
uuid |
User ID of the coordinator or admin who created the contact record. Immutable after creation. | required |
updated_by |
uuid |
User ID of the last user to modify this contact record. Updated on every write operation. | - |
created_at |
datetime |
UTC timestamp of record creation. Set server-side and immutable. | required |
updated_at |
datetime |
UTC timestamp of the last update. Maintained via Supabase database trigger on every write. | required |
Database Indexes
idx_contact_organization_id
Columns: organization_id
idx_contact_org_status
Columns: organization_id, status
idx_contact_org_name
Columns: organization_id, last_name, first_name
idx_contact_org_email
Columns: organization_id, email
idx_contact_is_deleted
Columns: organization_id, is_deleted
idx_contact_created_at
Columns: organization_id, created_at
idx_contact_notes_fts
Columns: organization_id, notes
Validation Rules
first_last_name_required
error
Validation failed
email_format
error
Validation failed
phone_format
error
Validation failed
organization_must_be_active
error
Validation failed
date_of_birth_not_future
error
Validation failed
chapter_count_max_five
error
Validation failed
notes_length_limit
error
Validation failed
status_transition_validity
error
Validation failed
Business Rules
org_data_isolation
Every contact record must be scoped to exactly one organization_id. Supabase RLS policies enforce that authenticated users can only read or write contacts belonging to their active organization session. No cross-organization contact access is permitted.
max_chapter_affiliations
A contact may belong to a maximum of 5 local chapters simultaneously (NHF requirement). Attempting to add a sixth chapter affiliation must be rejected with a user-visible error. This limit is enforced both at the service layer and in the UI form validator.
encrypted_field_authorization
The address_encrypted and medical_context_encrypted fields may only be decrypted for users holding a coordinator or organization-admin role for the owning organization. Peer mentors receive these fields in their encrypted form only when explicitly authorized per assignment. Decryption is logged.
screen_reader_sensitive_field_warning
Before any screen reader (VoiceOver, TalkBack) vocalizes address_encrypted or medical_context_encrypted content, the sensitive-field-warning-dialog must be displayed and explicitly acknowledged by the user. This warning may be suppressed for the remainder of the session after first acknowledgement.
soft_delete_only
Contact records are never physically deleted from the database. All delete operations set is_deleted=true and deleted_at=now(). This preserves referential integrity for activity records, assignments, and Bufdir reports that reference the contact.
read_receipt_on_field_reveal
Whenever a user explicitly reveals an encrypted field (address or medical context) in the UI, a read-receipt record must be written to Supabase with the user ID, field key, contact ID, and UTC timestamp. This supports Blindeforbundet's information security audit requirements.
cross_chapter_duplicate_detection
When a contact is affiliated with multiple chapters (NHF), the system must detect and warn if the same activity is registered for this contact by coordinators of different chapters within the same reporting period. This prevents double-counting in Bufdir submissions.
search_scope_isolation
Contact search results must always be filtered by the current user's organization_id. Full-text search on the notes field and ILIKE queries on name fields must include the organization_id predicate to prevent cross-tenant data leakage.
CRUD Operations
Storage Configuration
Entity Relationships
A contact may be assigned to peer mentors sequentially or simultaneously depending on organizational rules
A contact (especially in NHF) may belong to up to 5 local chapters simultaneously through the contact_chapter junction
All contacts are scoped to a single organization for multi-tenant data isolation via Supabase RLS