Zaptam Data Models
Complete Prisma schema documentation with 12 models and 13 enums.
Schema Overview
┌─────────────────────────────────────────────────────────────┐
│ User │
│ (Central entity - all other models relate to User) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ UserSettings │ │ Photo │ │ RefreshToken │ │
│ │ (1:1) │ │ (1:many) │ │ (1:many) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Interest │ │ Message │ │Conversation │ │
│ │ (sender/ │ │ (sender/ │ │(participant1/│ │
│ │ recipient) │ │ recipient) │ │ participant2)│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │Verification │ │ Report │ │ OtpCode │ │
│ │ (1:many) │ │(reporter/ │ │ (1:many) │ │
│ │ │ │ reported) │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ Wallet │ ┌──────────────────────────────────────┐│
│ │ Transaction │ │ MembershipTier ││
│ │ (1:many) │ │ (standalone, no FK) ││
│ └──────────────┘ └──────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘Models
User
The central model representing all platform users.
prisma
model User {
id String @id @default(uuid()) @db.Uuid
phoneNumber String @unique @map("phone_number")
email String? @unique
password String?
role UserRole @default(USER)
gender Gender?
status UserStatus @default(PENDING)
// Profile
alias String? @unique
name String?
bio String?
occupation String?
intro String?
netWorth String? @map("net_worth")
dateOfBirth DateTime? @map("date_of_birth")
// Trust & Verification
trustScore Int @default(50) @map("trust_score")
verificationLevel VerificationLevel @default(NONE) @map("verification_level")
// Timestamps
lastActiveAt DateTime? @map("last_active_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
deletedAt DateTime? @map("deleted_at")
// Relations
settings UserSettings?
photos Photo[]
sentMessages Message[] @relation("sender")
receivedMessages Message[] @relation("recipient")
sentInterests Interest[] @relation("sender")
receivedInterests Interest[] @relation("recipient")
verifications Verification[]
walletTransactions WalletTransaction[]
reports Report[] @relation("reporter")
reportedBy Report[] @relation("reported")
conversations1 Conversation[] @relation("participant1")
conversations2 Conversation[] @relation("participant2")
refreshTokens RefreshToken[]
otpCodes OtpCode[]
@@index([phoneNumber])
@@index([status])
@@index([gender])
@@map("users")
}Field Details
| Field | Type | Description |
|---|---|---|
id | UUID | Primary key |
phoneNumber | String | Unique, international format (+268...) |
email | String? | Optional, unique if provided |
password | String? | Bcrypt hash, null during pending registration |
role | UserRole | Permission level (default: USER) |
gender | Gender? | MALE or FEMALE |
status | UserStatus | Account status (default: PENDING) |
alias | String? | Display name, unique |
name | String? | Real name (rarely used) |
bio | String? | Profile description |
occupation | String? | For male users |
intro | String? | For female users |
netWorth | String? | Male users: '50k-100k', '100k-500k', '500k-1m', '1m-5m', '5m+' |
dateOfBirth | DateTime? | For age calculation |
trustScore | Int | 0-100, default 50 |
verificationLevel | VerificationLevel | NONE, IDENTITY, or FULL |
lastActiveAt | DateTime? | Updated on socket connection |
deletedAt | DateTime? | Soft delete timestamp |
UserSettings
Privacy and preferences for each user.
prisma
model UserSettings {
id String @id @default(uuid()) @db.Uuid
userId String @unique @map("user_id") @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
aliasMode Boolean @default(true) @map("alias_mode")
showOnlineStatus Boolean @default(false) @map("show_online_status")
regionMasking Boolean @default(true) @map("region_masking")
photoBlurLevel Int @default(100) @map("photo_blur_level")
disappearingMsgs Int? @map("disappearing_msgs_hours")
@@map("user_settings")
}Field Details
| Field | Type | Default | Description |
|---|---|---|---|
aliasMode | Boolean | true | Show alias instead of real name |
showOnlineStatus | Boolean | false | Let others see if online |
regionMasking | Boolean | true | Hide location/region |
photoBlurLevel | Int | 100 | Default blur for photos (0-100) |
disappearingMsgs | Int? | null | Auto-delete messages after N hours |
Photo
User profile photos.
prisma
model Photo {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
url String
blurLevel Int @default(100) @map("blur_level")
isPrimary Boolean @default(false) @map("is_primary")
order Int @default(0)
createdAt DateTime @default(now()) @map("created_at")
@@map("photos")
}Field Details
| Field | Type | Description |
|---|---|---|
url | String | Cloudflare R2 URL |
blurLevel | Int | 0 (clear) to 100 (fully blurred) |
isPrimary | Boolean | Main profile photo |
order | Int | Display order in gallery |
Interest
Express interest between users.
prisma
model Interest {
id String @id @default(uuid()) @db.Uuid
senderId String @map("sender_id") @db.Uuid
recipientId String @map("recipient_id") @db.Uuid
sender User @relation("sender", fields: [senderId], references: [id], onDelete: Cascade)
recipient User @relation("recipient", fields: [recipientId], references: [id], onDelete: Cascade)
status InterestStatus @default(PENDING)
createdAt DateTime @default(now()) @map("created_at")
@@unique([senderId, recipientId])
@@map("interests")
}Matching Logic
- User A expresses interest in User B → creates
PENDINGInterest (A→B) - User B expresses interest in User A → checks for existing Interest (A→B)
- If found and PENDING:
- Update A→B to
ACCEPTED - Create B→A as
ACCEPTED - Create Conversation
- It's a match!
- Update A→B to
Conversation
Container for messages between matched users.
prisma
model Conversation {
id String @id @default(uuid()) @db.Uuid
participant1Id String @map("participant1_id") @db.Uuid
participant2Id String @map("participant2_id") @db.Uuid
participant1 User @relation("participant1", fields: [participant1Id], references: [id], onDelete: Cascade)
participant2 User @relation("participant2", fields: [participant2Id], references: [id], onDelete: Cascade)
messages Message[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([participant1Id, participant2Id])
@@map("conversations")
}Message
Individual chat messages.
prisma
model Message {
id String @id @default(uuid()) @db.Uuid
conversationId String @map("conversation_id") @db.Uuid
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
senderId String @map("sender_id") @db.Uuid
sender User @relation("sender", fields: [senderId], references: [id], onDelete: Cascade)
recipientId String @map("recipient_id") @db.Uuid
recipient User @relation("recipient", fields: [recipientId], references: [id], onDelete: Cascade)
content String?
mediaUrl String? @map("media_url")
isRead Boolean @default(false) @map("is_read")
expiresAt DateTime? @map("expires_at")
deletedAt DateTime? @map("deleted_at")
createdAt DateTime @default(now()) @map("created_at")
@@index([conversationId])
@@index([senderId])
@@index([recipientId])
@@map("messages")
}Field Details
| Field | Type | Description |
|---|---|---|
content | String? | Text content (max 2000 chars) |
mediaUrl | String? | Attached media URL |
isRead | Boolean | Read receipt |
expiresAt | DateTime? | Disappearing message expiry |
deletedAt | DateTime? | Soft delete for sender |
Verification
Verification submissions and reviews.
prisma
model Verification {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
type VerificationType
status VerificationStatus @default(PENDING)
documents Json?
notes String?
reviewedBy String? @map("reviewed_by") @db.Uuid
reviewedAt DateTime? @map("reviewed_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("verifications")
}Verification Types
| Type | Gender | Purpose |
|---|---|---|
| IDENTITY | Both | ID verification (selfie + ID photo) |
| WORTH | Male | Proof of net worth |
| VALUE | Female | Profile value verification |
Progression
NONE → (IDENTITY approved) → IDENTITY → (WORTH/VALUE approved) → FULLWalletTransaction
Financial transactions.
prisma
model WalletTransaction {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
type TransactionType
amount Decimal @db.Decimal(10, 2)
currency String @default("USD")
status TransactionStatus @default(PENDING)
reference String?
createdAt DateTime @default(now()) @map("created_at")
@@index([userId])
@@map("wallet_transactions")
}Transaction Types
| Type | Direction | Description |
|---|---|---|
MEMBERSHIP | Debit | Monthly subscription |
CREDIT_PURCHASE | Credit | Buying credits (male) |
EARNING | Credit | Earnings from engagement (female) |
WITHDRAWAL | Debit | Withdrawing earnings (female) |
BOOST | Debit | Profile boost purchase |
Report
User reports for moderation.
prisma
model Report {
id String @id @default(uuid()) @db.Uuid
reporterId String @map("reporter_id") @db.Uuid
reporter User @relation("reporter", fields: [reporterId], references: [id], onDelete: Cascade)
reportedId String @map("reported_id") @db.Uuid
reported User @relation("reported", fields: [reportedId], references: [id], onDelete: Cascade)
type ReportType
description String
evidence Json?
status ReportStatus @default(PENDING)
resolution String?
resolvedBy String? @map("resolved_by") @db.Uuid
resolvedAt DateTime? @map("resolved_at")
createdAt DateTime @default(now()) @map("created_at")
@@map("reports")
}Report Types
| Type | Severity | Description |
|---|---|---|
BLACKMAIL | Critical | Extortion attempts |
HARASSMENT | High | Unwanted contact/messages |
FAKE_PROFILE | Medium | Suspected fake identity |
INAPPROPRIATE_CONTENT | Medium | Inappropriate photos/messages |
OTHER | Variable | Other violations |
MembershipTier
Subscription tiers (standalone, no user FK).
prisma
model MembershipTier {
id String @id @default(uuid()) @db.Uuid
name String
price Decimal @db.Decimal(10, 2)
currency String @default("USD")
duration Int
features Json
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
@@map("membership_tiers")
}Default Tiers
| Name | Price | Duration | Key Features |
|---|---|---|---|
| Standard | $49 | 30 days | Worth verification, basic access |
| Premium | $149 | 30 days | Worth badge, priority discovery |
| Elite | $499 | 30 days | Personal curator, 24/7 concierge |
RefreshToken
JWT refresh tokens.
prisma
model RefreshToken {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
token String @unique
expiresAt DateTime @map("expires_at")
createdAt DateTime @default(now()) @map("created_at")
@@index([userId])
@@index([token])
@@map("refresh_tokens")
}OtpCode
One-time passwords for verification.
prisma
model OtpCode {
id String @id @default(uuid()) @db.Uuid
userId String? @map("user_id") @db.Uuid
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
phone String
code String
purpose OtpPurpose
expiresAt DateTime @map("expires_at")
usedAt DateTime? @map("used_at")
createdAt DateTime @default(now()) @map("created_at")
@@index([phone, code])
@@map("otp_codes")
}Enums
UserRole
prisma
enum UserRole {
USER // Regular user
CURATOR // Can review verifications, reports
ADMIN // Can manage users, suspend/ban
SUPER_ADMIN // Full access
}Role Hierarchy: USER (1) < CURATOR (2) < ADMIN (3) < SUPER_ADMIN (4)
Gender
prisma
enum Gender {
MALE
FEMALE
}UserStatus
prisma
enum UserStatus {
PENDING // Registration incomplete
ACTIVE // Full access
SUSPENDED // Temporarily restricted
BANNED // Permanently blocked
DELETED // Soft deleted
}VerificationLevel
prisma
enum VerificationLevel {
NONE // No verification
IDENTITY // ID verified
FULL // ID + worth/value verified
}VerificationType
prisma
enum VerificationType {
IDENTITY // ID document + selfie
WORTH // Net worth proof (male)
VALUE // Profile value (female)
}VerificationStatus
prisma
enum VerificationStatus {
PENDING // Awaiting review
APPROVED // Verified
REJECTED // Failed verification
}InterestStatus
prisma
enum InterestStatus {
PENDING // Interest expressed, awaiting reciprocation
ACCEPTED // Mutual match
DECLINED // Rejected
}TransactionType
prisma
enum TransactionType {
MEMBERSHIP // Subscription payment
CREDIT_PURCHASE // Buying credits
EARNING // Platform earnings
WITHDRAWAL // Cashing out
BOOST // Profile boost
}TransactionStatus
prisma
enum TransactionStatus {
PENDING // Processing
COMPLETED // Success
FAILED // Failed
REFUNDED // Reversed
}ReportType
prisma
enum ReportType {
BLACKMAIL // Extortion
HARASSMENT // Unwanted contact
FAKE_PROFILE // Suspected fake
INAPPROPRIATE_CONTENT // Violations
OTHER // Miscellaneous
}ReportStatus
prisma
enum ReportStatus {
PENDING // New report
INVESTIGATING // Under review
RESOLVED // Action taken
DISMISSED // No action needed
}OtpPurpose
prisma
enum OtpPurpose {
REGISTRATION // New account
LOGIN // Login verification
PASSWORD_RESET // Reset password
}Database Indexes
| Table | Index | Purpose |
|---|---|---|
| users | phone_number | Fast login lookup |
| users | status | Filter active users |
| users | gender | Discovery filtering |
| messages | conversation_id | Message retrieval |
| messages | sender_id | Sent messages |
| messages | recipient_id | Received messages |
| wallet_transactions | user_id | User's transactions |
| refresh_tokens | user_id | User's tokens |
| refresh_tokens | token | Token validation |
| otp_codes | phone, code | OTP lookup |
Cascade Deletes
All user-related models use onDelete: Cascade, meaning:
- When a User is deleted, all related records are automatically deleted
- This includes: settings, photos, messages, interests, verifications, transactions, reports, tokens, OTPs