Skip to content

Domain Model

Both entities are defined in @nx/core under the outreach PostgreSQL schema and re-exported into this package. There are no foreign keys between them - each is an independent capture table.

1. Full ERD

2. Entities

Inquiry

PropertyValue
Tableoutreach.Inquiry
Sourcepackages/core/src/models/schemas/outreach/inquiry/schema.ts
Soft-deleteyes (deletedAt)
Owner ID column- (global, not merchant-scoped)
Hidden propertiesdeletedAt

Fields:

FieldTypeRequiredDefaultDescription
idtextSnowflakePrimary key
typetext000_CONSULTSource channel - see enum
statustextNEWPipeline state - see enum
firstNametext-Contact first name
lastNametext-Contact last name
emailtext-Contact email (indexed)
phonetext-Contact phone
businessNametext-Lead's business
businessTypetext-Business category
locationCounttext-Lead qualification
estimatedRevenuetext-Lead qualification
subjecttext-Message subject
messagetext-Free-text body
assignedTotext-Owning admin/user id
repliedAttimestamptz-First-reply timestamp
repliedBytext-Replying user id
convertedAttimestamptz-Conversion timestamp
lostReasontext-Lost-deal reason
notetext-Internal note
createdAttimestamptznow()-

Type enum (InquiryTypes):

ValueDescription
000_CONSULTGeneral consultation / unknown source (default)
100_CONTACTContact form (/contact)
200_SALESSales inquiry (/contact-sales)
300_DEMODemo request (/demo)
400_PARTNERPartner application (/partners)

Status enum (InquiryStatuses, IGNIS Statuses):

ValueDescription
NEWJust submitted
PROCESSINGPicked up by admin
COMPLETEDConverted
CLOSEDResolved without conversion
CANCELLEDSpam / invalid / abandoned

Indexes & constraints:

NameColumnsType
PK_InquiryidPrimary key
IDX_Inquiry_typetypeBtree
IDX_Inquiry_statusstatusBtree
IDX_Inquiry_emailemailBtree

Relations: none.

Subscriber

PropertyValue
Tableoutreach.Subscriber
Sourcepackages/core/src/models/schemas/outreach/subscriber/schema.ts
Soft-deleteyes (deletedAt)
Owner ID column- (global)
Hidden propertiesdeletedAt, unsubscribeToken (never returned by the model API)

Fields:

FieldTypeRequiredDefaultDescription
idtextSnowflakePrimary key
emailtext-Unique (partial, where not deleted)
localetextviNewsletter language (vi / en)
topicsjsonb (string[])['all']Topic preferences
statustextACTIVATEDSee enum
unsubscribeTokentextSnowflake ($defaultFn)Token for unsubscribe link; hidden from API
subscribedAttimestamptznow()First/last subscribe time
unsubscribedAttimestamptz-Set on unsubscribe, cleared on re-subscribe
createdAttimestamptznow()-

Status enum (SubscriberStatuses, IGNIS Statuses):

ValueDescription
ACTIVATEDActive subscription
DEACTIVATEDUnsubscribed
ARCHIVEDAdmin-archived (valid status, not produced by the service flow)

Indexes & constraints:

NameColumnsType
PK_SubscriberidPrimary key
UPI_Subscriber_emailemail (where deletedAt IS NULL)Unique partial index
IDX_Subscriber_statusstatusBtree
IDX_Subscriber_unsubscribeTokenunsubscribeTokenBtree

Relations: none.

3. Cross-entity Invariants

InvariantEnforcement
One live Subscriber per emailUnique partial index UPI_Subscriber_email (where deletedAt IS NULL) + SubscriberService.subscribe() find-then-reactivate
unsubscribeToken unique enough to be unguessableSnowflake $defaultFn (64-bit, worker 10) - see ADR-0001
Re-subscribe clears unsubscribedAtSubscriberService.subscribe() sets unsubscribedAt: null on reactivate

4. Soft-delete Behavior

BehaviorDetail
Read defaultdeletedAt IS NULL (model defaultFilter)
Hard-deleteNever by default; admin CRUD deleteById performs soft-delete
RestoreNot exposed via API
Email uniquenessEnforced only over non-deleted rows (partial unique index)

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.