Skip to content

Eneza Database Models (Prisma)

The Eneza API uses PostgreSQL with Prisma ORM. The schema contains 50+ models organized into logical domains.

Database Overview

DomainTablesDescription
Reference7Countries, cities, categories, pricing
Users8Users, advertisers, referrals, achievements
Ads8Ads, stats, demographics, tracks
Subscriptions3Subscriptions, screenshots, billing
Fraud3Fraud scores, history, bans
Finance4Transactions, invoices, deposits
Notifications4Notifications, broadcasts, device tokens
Admin6Admin users, permissions, activities
System5Processing jobs, exchange rates, outbox

Reference Tables

PricingTier

Defines pricing tiers for different markets.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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).

prisma
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.

prisma
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

Advertisement campaigns.

prisma
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.

prisma
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.

prisma
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).

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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.

prisma
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())
}

One chat. Everything done.