Eneza Database Models (Prisma)
The Eneza API uses PostgreSQL with Prisma ORM. The schema contains 50+ models organized into logical domains.
Database Overview
| Domain | Tables | Description |
|---|---|---|
| Reference | 7 | Countries, cities, categories, pricing |
| Users | 8 | Users, advertisers, referrals, achievements |
| Ads | 8 | Ads, stats, demographics, tracks |
| Subscriptions | 3 | Subscriptions, screenshots, billing |
| Fraud | 3 | Fraud scores, history, bans |
| Finance | 4 | Transactions, invoices, deposits |
| Notifications | 4 | Notifications, broadcasts, device tokens |
| Admin | 6 | Admin users, permissions, activities |
| System | 5 | Processing jobs, exchange rates, outbox |
Reference Tables
PricingTier
Defines pricing tiers for different markets.
model PricingTier {
id String @id @default(uuid())
name String // "Premium Markets", "Large Markets", "Emerging Markets"
key String @unique // tier1, tier2, tier3
cpmUsd Decimal // Cost per 1000 views in USD
posterRatePerThousandViews Decimal // Payout to poster per 1000 views
minimumBudgetUsd Decimal // Minimum campaign budget
isDefault Boolean @default(false)
isActive Boolean @default(true)
// Relations
countries Country[]
ads Ad[]
}Country
Supported countries with localization settings.
model Country {
id String @id @default(uuid())
code String @unique // ISO code (SZ, ZA, KE)
name String // "Eswatini", "South Africa"
callingCode String // "+268", "+27"
currencyCode String // "SZL", "ZAR"
defaultLanguage String @default("en_us")
minimumWithdrawal Decimal // Minimum withdrawal in local currency
phoneNumberLength Int // Expected phone number length
pricingTierId String? // FK to PricingTier
isActive Boolean @default(true)
// Relations
pricingTier PricingTier?
cities City[]
users User[]
advertisers Advertiser[]
ads Ad[]
}City
Cities within countries for demographic targeting.
model City {
id String @id @default(uuid())
name String // "Mbabane", "Johannesburg"
countryId String
population Int?
priceMultiplier Decimal? // Optional local price adjustment
// Relations
country Country
users User[]
}Category
Ad categories with age restrictions.
model Category {
id String @id @default(uuid())
title String // "Food & Drink", "Technology"
ageMin Int // Minimum age to view
ageMax Int // Maximum age to view
icon String? // Category icon URL
isActive Boolean @default(true)
// Relations
ads Ad[]
}PaymentProcessor
Mobile money providers for user payouts.
model PaymentProcessor {
id String @id @default(uuid())
name String // "MTN MoMo", "EWallet"
adminContact String
email String
phoneNumber String
logoUrl String?
downloadUrl String? // App store link
uniqueIdentifierType String @default("phoneNumber")
instructions String[] // Setup instructions
isActive Boolean @default(true)
// Relations
countries PaymentProcessorCountry[]
users User[]
}User Tables
User
Mobile app users (posters).
model User {
id String @id @default(uuid())
fullName String?
phoneNumber String @unique
password String? // Optional (OTP-based auth)
cityId String?
countryId String
userBalance Decimal @default(0) // Earnings balance
currency String @default("USD")
hasUnreadNotifications Boolean @default(false)
isKycVerified Boolean @default(false)
displayImg String?
dob DateTime?
gender String? // "male", "female"
isDisabled Boolean @default(false)
isConfirmed Boolean @default(false)
// Referral system
referralCode String? @unique
referredById String?
// OTP rate limiting
otpAttempts Int @default(0)
otpLockedUntil DateTime?
otpLastAttempt DateTime?
// Geo verification
registrationIp String?
registrationCountry String?
// Payment
paymentIdentifier String? @unique
paymentProcessorId String?
// Relations
country Country
city City?
paymentProcessor PaymentProcessor?
referredBy User? @relation("UserReferrals")
referredUsers User[] @relation("UserReferrals")
transactions Transaction[]
subscriptions Subscription[]
screenshots Screenshot[]
achievements UserAchievement[]
streak UserStreak?
stats UserStats?
}Advertiser
Businesses that create ad campaigns.
model Advertiser {
id String @id @default(uuid())
companyName String?
phone String?
countryId String?
countryCode String? // ISO code
usdBalance Decimal @default(0) // Campaign budget balance
contactPerson String
emailAddress String @unique
address String?
city String?
state String?
zip String?
status String @default("active")
isVerified Boolean @default(false)
isEmailVerified Boolean @default(false)
password String
// Email verification
verificationToken String?
resetToken String?
resetTokenExpiration DateTime?
// OTP rate limiting
otpAttempts Int @default(0)
otpLockedUntil DateTime?
// Invoice details
taxId String?
signupMethod String @default("email")
// Drip campaign
dripCampaignState Json? // Tracks which emails sent
emailPreferences Json?
lastEmailSentAt DateTime?
// Relations
country Country?
ads Ad[]
invoices Invoice[]
deposits AdvertiserDeposit[]
}Ad Tables
Ad
Advertisement campaigns.
model Ad {
id String @id @default(uuid())
title String?
advertiserId String
categoryId String?
countryId String?
postedOn DateTime @default(now())
expiresOn DateTime?
adThumbnail String?
active Boolean @default(false)
video String? // Processed video URL
status String @default("UNDER_REVIEW")
// Campaign settings
repostTimes Int @default(99)
startDate DateTime?
endDate DateTime?
stoppedAt DateTime?
campaignDays Int?
// Links
websiteUrl String? // Advertiser's destination URL
ctaLink String? // Tracking link
whatsappNumber String?
shortCode String? @unique
// Video processing
videoProcessed Boolean @default(false)
isProcessing Boolean @default(false)
sourceVideoUrl String?
videoVariants Json? // {"1080p": {url, width, height}, ...}
// AI moderation
aiProcessingStatus String @default("pending")
aiProcessingError String?
tosFlags String[]
tosConfidence Decimal?
tosViolations String[]
aiGeneratedTitle String?
// USD-based pricing
priceUsd Decimal @default(0) // Campaign budget
guaranteedViews Int @default(0) // Promised views
deliveredViews Int @default(0) // Actual views
effectiveCpm Decimal @default(0) // CPM at purchase
pricingTierId String?
// Refunds
isRefundProcessed Boolean @default(false)
refundedAmount Decimal?
// Soft delete
isDeleted Boolean @default(false)
deletedAt DateTime?
deletionReason String?
// Relations
advertiser Advertiser
category Category?
country Country?
pricingTier PricingTier?
demographics AdDemographics?
stats AdStats?
moderation AdModeration?
subscriptions Subscription[]
}AdStats
Real-time campaign statistics.
model AdStats {
id String @id @default(uuid())
adId String @unique
downloadCount Int @default(0)
subscriptionCount Int @default(0)
screenshotUploadCount Int @default(0)
totalViews Int @default(0)
estimatedExpectedViews Int @default(0)
verifiedViews Int @default(0)
balanceViews Int @default(0)
// Relations
ad Ad
}AdDemographics
Targeting configuration for ads.
model AdDemographics {
id String @id @default(uuid())
adId String @unique
ageMin Int
ageMax Int
countryId String?
gender String @default("both") // male, female, both
// Relations
ad Ad
country Country?
cities AdDemographicsCity[]
}Subscription Tables
Subscription
User subscription to an ad (download).
model Subscription {
id String @id @default(uuid())
userId String
adId String
status String @default("AWAITING_SCREENSHOT")
subscribeTime DateTime @default(now())
isActive Boolean @default(true)
code String // Unique QR code
generateVideoStatus String @default("PROCESSING")
personalizedVideoUrl String? // Video with watermark
phoneInfo Json? // Device info
// Geo verification
ipAddress String?
ipCountry String?
// Reminders
screenshotReminderSent Boolean @default(false)
// Relations
user User
ad Ad
screenshots Screenshot[]
}Screenshot
Screenshot proof submission.
model Screenshot {
id String @id @default(uuid())
userId String
submitTime DateTime
screenshotTime DateTime
adId String
subscriptionId String
viewsCount Int // AI-verified views
status String @default("PENDING")
timeSinceSubscribe Int // Seconds since subscription
phoneInfo Json
submittedViews Int? // User-claimed views
isApproved Boolean @default(false)
reason String? // Rejection reason
screenshotLink String
verificationChecks Json? // AI check results
confidenceScore Decimal? // 0.00 - 1.00
// Payment
acceptedViews Int?
payoutAmountUsd Decimal?
// Geo
ipAddress String?
ipCountry String?
// Support
moreProofNeeded Boolean @default(false)
videoProofLink String?
rejectionReason String?
// Relations
user User
ad Ad
subscription Subscription
fraudScores FraudScore[]
}Fraud Detection Tables
FraudScore
Per-screenshot fraud analysis results.
model FraudScore {
id String @id @default(uuid())
userId String
screenshotId String
subscriptionId String
scores Json // Individual check scores
checkDetails Json? // Detailed check results
aggregateScore Decimal // 0.0000 - 1.0000
decision String // AUTO_APPROVE, MANUAL_REVIEW, AUTO_REJECT, AUTO_BAN
criticalFailures String[] // List of critical failures
isCriticalFailure Boolean @default(false)
// Relations
user User
screenshot Screenshot
subscription Subscription
}UserFraudHistory
Accumulated fraud metrics per user.
model UserFraudHistory {
id String @id @default(uuid())
userId String @unique
totalSubmissions Int @default(0)
approvedCount Int @default(0)
rejectedCount Int @default(0)
approvalRate Decimal @default(0)
criticalFailureCount Int @default(0)
criticalFailures Json? // Detailed failure records
riskScore Decimal @default(0) // 0.00 - 1.00
flaggedPatterns String[]
linkedDevices Json?
linkedIPs Json?
avgVerifiedViews Decimal @default(0)
maxVerifiedViews Int @default(0)
// Relations
user User
}BanRecord
User ban history.
model BanRecord {
id String @id @default(uuid())
userId String
reason String // FRAUD_DETECTED, etc.
evidence Json // Supporting evidence
relatedUserIds String[] // Network bans
bannedAt DateTime @default(now())
bannedBy String // 'SYSTEM' or admin ID
appealStatus String @default("NONE")
appeal Json? // Appeal details
unbannedAt DateTime?
unbannedBy String?
unbanReason String?
isActive Boolean @default(true)
// Relations
user User
}Finance Tables
Transaction
User payout transactions.
model Transaction {
id String @id @default(uuid())
amount Decimal
amountUsd Decimal
amountLocal Decimal?
userId String
status String @default("pending")
submitTime DateTime @default(now())
paymentConfirmationCode String?
paymentTime DateTime?
paymentMethodId String?
code String @unique
currency String
exchangeRate Decimal?
type String @default("withdrawal")
adId String?
description String?
// Relations
user User
paymentMethod PaymentProcessor?
ad Ad?
}Invoice
Advertiser invoices.
model Invoice {
id String @id @default(uuid())
invoiceNumber String @unique // INV-YYYY-NNNNN
advertiserId String
adId String
amountUsd Decimal
amountLocal Decimal
currency String
exchangeRate Decimal
campaignDays Int
dailyBudgetUsd Decimal
estimatedViews Int
customerName String
companyName String?
taxId String?
email String?
status String @default("pending")
paymentMethod String?
paymentReference String?
paymentUrl String?
sentViaEmail Boolean @default(false)
paidAt DateTime?
refundedAmount Decimal @default(0)
// Relations
advertiser Advertiser
ads Ad[]
}Admin Tables
AdminUser
Internal admin users.
model AdminUser {
id String @id @default(uuid())
email String @unique
password String
fullName String
role String @default("support")
status String @default("active")
permissions String[] @default([])
lastLoginAt DateTime?
createdAt DateTime @default(now())
// Invitation
invitedById String?
invitationToken String?
invitationStatus String @default("pending")
// Relations
invitedBy AdminUser? @relation("AdminInvitations")
invitees AdminUser[] @relation("AdminInvitations")
activities AdminActivity[]
}AdminActivity
Admin action audit log.
model AdminActivity {
id String @id @default(uuid())
adminId String
action String // user.ban, ad.approve, etc.
resourceType String // user, ad, screenshot
resourceId String?
details Json?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
// Relations
admin AdminUser
}Notification Tables
DeviceToken
Firebase push notification tokens.
model DeviceToken {
id String @id @default(uuid())
userId String
token String @unique
platform String // android, ios
isActive Boolean @default(true)
createdAt DateTime @default(now())
// Relations
user User
}Notification
In-app notifications.
model Notification {
id String @id @default(uuid())
userId String
title String
body String
type String // screenshot_approved, payment_received
data Json?
isRead Boolean @default(false)
createdAt DateTime @default(now())
// Relations
user User
}System Tables
ProcessingJob
Async processing job tracking.
model ProcessingJob {
id String @id @default(uuid())
jobType String // video, screenshot_verification
adId String?
subscriptionId String?
status String @default("pending")
attempts Int @default(0)
maxAttempts Int @default(3)
lastError String?
result Json?
startedAt DateTime?
completedAt DateTime?
scheduledAt DateTime @default(now())
// Relations
ad Ad?
}ExchangeRate
Currency exchange rates.
model ExchangeRate {
id String @id @default(uuid())
currencyCode String @unique
rateToUsd Decimal
source String @default("swychr")
fetchedAt DateTime @default(now())
}MessageOutbox
Transactional outbox for reliable messaging.
model MessageOutbox {
id String @id @default(uuid())
topic String
payload Json
attributes Json?
status String @default("pending")
attempts Int @default(0)
maxAttempts Int @default(5)
lastError String?
processedAt DateTime?
scheduledAt DateTime @default(now())
createdAt DateTime @default(now())
}