Contact
Data Entity
Description
A person being supported by a peer mentor — the primary beneficiary of the peer mentoring program. Contacts have personal details, associated activities, notes, and caregiver relationships. Contact data is role-scoped: peer mentors see only their own contacts, coordinators see all contacts in their association.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Globally unique identifier for the contact record, generated server-side on creation | PKrequiredunique |
local_association_id |
uuid |
Foreign key to local_associations — defines which local association this contact belongs to, used as the primary scope boundary for role-based access control and Bufdir reporting aggregation | required |
created_by_user_id |
uuid |
Foreign key to users — the peer mentor or coordinator who created this contact record. Used to enforce peer-mentor-scoped visibility (peer mentors only see contacts they created) | required |
first_name |
string |
Contact's given name. Required for identification in the contact list, activity wizard contact step, and coordinator reports | required |
last_name |
string |
Contact's family name. Required for full-name display and FTS5 search indexing | required |
phone |
string |
Contact's primary phone number in E.164 format (e.g. +4791234567). Used by peer mentors to initiate contact and displayed in caregiver detail screens | - |
email |
string |
Contact's email address. Optional — many contacts (elderly, low-digital-literacy) may not have one. Stored lowercased | - |
address |
text |
Full postal address as a freeform text field. Used by Location Service for geographic matching (geocoding to lat/lng for coordinator map view) and by Blindeforbundet coordinators when dispatching encrypted assignments | - |
date_of_birth |
datetime |
Contact's date of birth (date portion only, stored as timestamp at midnight UTC). Used to compute age for Bufdir demographic reporting and to identify contacts requiring caregiver relationships (children, elderly) | - |
gender |
enum |
Contact's gender identity. Collected for Bufdir demographic reporting which requires participant breakdown by gender category | - |
preferred_contact_method |
enum |
How the contact prefers to be reached by their peer mentor. Informs the activity registration wizard defaults and coordinator matching | - |
is_active |
boolean |
Whether this contact is currently active and available for activity registration. Inactive contacts are hidden from the activity wizard contact step but remain accessible in contact detail for historical records | required |
notes_preview |
string |
Cached snippet of the most recent note for display in contact card widgets and list screens. Populated server-side after note creation/update. Not user-editable directly — updated by Notes Service | - |
last_activity_at |
datetime |
Timestamp of the most recent activity registered for this contact. Denormalized field updated after each activity save for efficient sorting in contact list screens without joins. Used by the scenario-based reminder system to detect contacts with no recent interaction (10-day follow-up alert) | - |
created_at |
datetime |
Timestamp when the contact record was first created. Set server-side, immutable | required |
updated_at |
datetime |
Timestamp of the most recent update to any contact field. Updated automatically on every write | required |
deleted_at |
datetime |
Soft-delete timestamp. NULL means active; a non-null value means the contact has been soft-deleted (GDPR erasure or coordinator deactivation). Soft-deleted contacts are excluded from all normal queries but retained for audit log integrity until the retention period expires | - |
Database Indexes
idx_contact_local_association_id
Columns: local_association_id
idx_contact_created_by_user_id
Columns: created_by_user_id
idx_contact_local_association_last_name
Columns: local_association_id, last_name
idx_contact_deleted_at
Columns: deleted_at
idx_contact_last_activity_at
Columns: last_activity_at
idx_contact_local_association_active
Columns: local_association_id, is_active, deleted_at
idx_contact_fts5_name
Columns: first_name, last_name
Validation Rules
first_name_required
error
Validation failed
last_name_required
error
Validation failed
phone_e164_format
error
Validation failed
email_format
error
Validation failed
date_of_birth_not_future
error
Validation failed
date_of_birth_minimum
error
Validation failed
local_association_exists
error
Validation failed
at_least_one_contact_detail
warning
Validation failed
update_requires_role_permission
error
Validation failed
address_length
error
Validation failed
Business Rules
role_scoped_visibility
Peer mentors may only read and write contacts that they created (created_by_user_id = current user). Coordinators can access all contacts within their local_association_id scope. Organization admins and global admins have read access across their full organizational scope. This rule is enforced at both the service layer and via SQL WHERE clauses in the repository.
local_association_immutable_after_create
The local_association_id of a contact cannot be changed after creation. Moving a contact between associations would break activity, note, and assignment history integrity and cross-association data isolation.
soft_delete_only
Contacts must never be hard-deleted. Deletion sets deleted_at to the current timestamp. Soft-deleted contacts are excluded from all list and search queries but retained for activity history integrity, audit logs, and GDPR data subject access requests.
last_activity_at_denormalization
The last_activity_at field must be updated whenever a new activity is created, updated, or deleted for this contact. This denormalization enables efficient sorting in contact list screens and powers the 10-day no-contact reminder scenario without requiring an expensive join on the activities table.
notes_preview_cache_invalidation
The notes_preview field must be refreshed whenever a note linked to this contact is created, updated, or soft-deleted. The Notes Service is responsible for triggering this update after successful note persistence.
multi_coordinator_duplicate_detection
When registering an activity for a contact, the Duplicate Detection Service checks whether the same peer mentor has already registered an activity with the same contact on the same date and of the same activity type (with a ±1 day fuzzy window). This addresses NHF's requirement to prevent double-reporting across multiple coordinators in the same local association.
caregiver_relationship_required_for_minors
For organizations supporting children (Barnekreftforeningen) or contacts identified as minors based on date_of_birth, the system emits a warning if a caregiver record has not been associated. Not a hard block — coordinators can dismiss and proceed.
offline_queue_sync
Contact create and update operations performed while the device is offline are enqueued in the offline queue and replicated to PostgreSQL on next connectivity restore via the Sync Service. Conflict resolution uses server-wins policy.
CRUD Operations
Storage Configuration
Entity Relationships
A contact (person being supported) is the subject of many activities registered by peer mentors
A contact may be the subject of multiple encrypted assignments dispatched to different peer mentors
A contact can have multiple caregivers or next-of-kin associated with them
A contact is the subject of many notes capturing qualitative context from interactions