Skip to content

Data Models

All models use Mongoose ODM on MongoDB Atlas.

Core Graph Nodes

User

Registered Pathorah accounts. The user node type in the graph. Has warmth/trust connections to other Users and Contacts, Employment records to Companies/Branches, and personal connections.

Contact

People in a user's address book. May or may not be registered Pathorah users. The contact node type. Participates in the graph identically to Users — Employment, connections, and all edge types apply.

Company

Company {
  name: string (required)
  slug: string (unique, lowercase, indexed)
  aliases: string[] (indexed — historical names from backfill)
  legalName?: string
  domain?: string (sparse indexed — used for representative email OTP)
  description?: string
  logoUrl, coverUrl, website?: string
  headquarters: { country, region, city, coords: [lng, lat] (2dsphere) }
  foundedYear?: number
  sizeBucket?: '1-10' | '11-50' | '51-200' | '201-500' | '501-1000' | '1001-5000' | '5000+'
  industries: ObjectId[] -> Industry
  capabilities: ObjectId[] -> Capability
  products: ObjectId[] -> Product
  markets: ObjectId[] -> Market
  certifications: ObjectId[] -> Certification
  verified: boolean (true when first tier-2 rep is confirmed)
  claimedByUserId?: ObjectId -> User
  source: 'user-claimed' | 'backfill' | 'admin' | 'enrichment'
}

Text index: name + aliases + description.

Branch

Branch {
  companyId: ObjectId -> Company (required, indexed)
  name: string (required)
  slug: string (unique per company)
  kind: 'headquarters' | 'office' | 'store' | 'factory' | 'warehouse' | 'virtual'
  location: { country, region, city, address, coords: [lng, lat] (2dsphere) }
  timezone?: string
  phone, email?: string
  managerUserId?: ObjectId -> User
  parentBranchId?: ObjectId -> Branch (for branch hierarchies)
  isActive: boolean
}

Edge Models

Employment

Links a person (User or Contact — exactly one, enforced by pre-validate hook) to a company (Company) or branch (Branch).

Employment {
  userId?: ObjectId -> User    (XOR with contactId)
  contactId?: ObjectId -> Contact
  companyId: ObjectId -> Company (required)
  branchId?: ObjectId -> Branch

  title, department?: string
  seniority?: 'intern'|'junior'|'mid'|'senior'|'lead'|'manager'|'director'|'vp'|'c-level'|'founder'
  employmentType?: 'full-time'|'part-time'|'contract'|'advisor'|'board'|'intern'

  startDate: Date (required)
  endDate?: Date
  isCurrent: boolean (auto-set false when endDate is given)

  isCompanyAdmin: boolean          -- permissions concept: can edit company page
  isRepresentative: boolean        -- graph concept: person IS the company

  representativeTier: 0 | 1 | 2   -- verification level; 0 = self-declared (no boost)
  representativeVerifiedAt?: Date
  representativeVerifiedByMethod?: 'email' | 'document' | 'vouch' | 'admin'
  representativeVerifiedByUserId?: ObjectId -> User

  representativeEmailOtpHash?: string (select: false — never returned by queries)
  representativeEmailOtpExpiresAt?: Date (select: false)
  representativeEmailAddress?: string
  representativeEmailAttempts?: number

  representativeDocumentUrl?: string
  representativeDocumentReviewStatus?: 'pending' | 'approved' | 'rejected'
  representativeDocumentReviewNote?: string

  source: 'self-declared'|'company-verified'|'imported'|'inferred'|'backfill'
  confidence: number (0-1)
  verifiedByCompanyAdmin: boolean
}

Unique indexes: (userId, companyId, startDate) partial; (contactId, companyId, startDate) partial.

CompanyRelationship

CompanyRelationship {
  fromCompanyId: ObjectId -> Company (required)
  toCompanyId: ObjectId -> Company (required, validated ≠ fromCompanyId)
  type: 'PARTNERS_WITH'|'SUPPLIES'|'CUSTOMER_OF'|'COMPETES_WITH'|'ACQUIRED'|'INVESTED_IN'|'SUBSIDIARY_OF'|'JV_WITH'
  direction: 'directed' | 'undirected'
  strength: number (0-1)
  startDate, endDate?: Date
  isActive: boolean
  evidenceUrls: string[]
  source?: 'user'|'admin'|'enrichment'|'public'
  createdByUserId?: ObjectId -> User
}

Unique on (fromCompanyId, toCompanyId, type, startDate).

KnowledgeEdge

Generic edge for future entity types and AI-enrichment relationships.

KnowledgeEdge {
  fromType: EntityType  -- 'user'|'contact'|'company'|'branch'|'industry'|'capability'|'product'|'market'|'certification'
  fromId: ObjectId
  toType: EntityType
  toId: ObjectId
  relation: string      -- arbitrary label, e.g. 'HAS_INDUSTRY', 'SERVES_MARKET'
  weight: number
  metadata?: object
  source?: string
  confidence?: number
  isActive: boolean
}

Taxonomy Collections

Tag collections used to enrich Company profiles. All follow the same simple schema { name, slug, description? }.

CollectionModel
industriesIndustry
capabilitiesCapability
productsProduct
marketsMarket
certificationsCertification

Other Models

ModelPurpose
ConnectionPersonal connection between two persons with warmth/trust/recency signals
ConnectionTypeUser-defined relationship labels
IntroductionRequestA warm intro request along a found path
PathSaved/cached path result
NotificationIn-app notifications
Message / ConversationMessaging between users
Post / CommentSocial feed
UserSubscriptionSubscription tier record
CreditTransactionCredit debit/credit ledger
UserReputationReputation signals
CircleGroup/team pathfinding scope
Report / GeneratedReportAdmin reporting
IntelligenceAlertAI-generated network alerts
RefreshTokenAuth refresh token store
PasswordResetPassword reset tokens
EmailVerificationEmail verification tokens
PricingConfigRuntime pricing configuration
LandingContent / LandingPageStatsCMS content for the landing page
BlockedUserSafety block list
AgentAutomated agent configurations