Skip to main content
The email resource is a canonically-stored email address shared across the financial graph. It functions as an atomic contact channel rather than a top-level business entity: a single Email row holds exactly one RFC 5321 address and is linked to companies via the CompanyEmail pivot and to people via the PersonEmail pivot — both pivots carry metadata (is_primary, is_verify, label). The record is workspace-scoped through an optional @ManyToOne to Workspace, and is deduplicated per workspace via a find-or-create pattern keyed on (email, workspace_pk, deleted_at IS NULL).
Read access today: this object is readable via the universal POST /v1/records/query endpoint with root: "emails". A dedicated GET /v1/emails endpoint is Planned.

API operations

Data model

Attributes

Relationships

System-computed

  • email_id is generated by gen_random_uuid() at the database default level and also explicitly set in EmailRepository.findOrCreate() via Node.js randomUUID() to ensure the value is available before flush.
  • created_at is set on insert by MikroORM onCreate: () => new Date().
  • updated_at is set on both insert and update by MikroORM onCreate/onUpdate lifecycle hooks.
  • deleted_at is null on creation; set to the current timestamp by EmailRepository.delete() as a soft-delete operation. Live queries must always filter deleted_at: null.
  • Find-or-create deduplication: EmailRepository.findOrCreate() normalizes the input address via .trim().toLowerCase(), then queries (email, workspace, deleted_at: null). When a workspace is not provided the scope falls back to (email, deleted_at: null) globally. A new Email row is only created when no matching live record is found.
  • Composite views: the emails records root exposes two composites — composite_companies_list (relation_list of company_id + company.name, via company_emails reverse) and composite_people_list (relation_list of person_id + full_name, via person_emails reverse). These are defined in composites.yml and reconstructed at query time.
  • Hasura RLS for the user role filters deleted_at IS NULL and resolves workspace tenancy indirectly: an email is visible if any linked company_email or person_email traces to a workspace matching X-Hasura-Workspace-Id (direct or parent-workspace cascade via workspace_group).

Example

Source: apps/api/src/database/entities/Email.ts · domain: financial-graph · tier: Supporting