generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } enum TenantStatus { ACTIVE SUSPENDED TRIAL INACTIVE } enum UserStatus { ACTIVE INVITED DISABLED } enum RoleCode { SUPER_ADMIN ADMIN_CLIENT AGENT } enum ChannelStatus { CONNECTED DISCONNECTED PENDING ERROR } enum ConversationStatus { OPEN PENDING RESOLVED ARCHIVED SPAM } enum ConversationPriority { LOW NORMAL HIGH URGENT } enum MessageDirection { INBOUND OUTBOUND } enum AuthTokenType { PASSWORD_RESET INVITE_ACCEPTANCE } enum MessageType { TEXT IMAGE VIDEO AUDIO DOCUMENT TEMPLATE INTERACTIVE } enum DeliveryStatus { QUEUED SENT DELIVERED READ FAILED } enum OptInStatus { UNKNOWN OPTED_IN OPTED_OUT } enum TemplateApprovalStatus { DRAFT PENDING APPROVED REJECTED DISABLED } enum CampaignStatus { DRAFT SCHEDULED PROCESSING COMPLETED PARTIAL_FAILED FAILED CANCELED } enum CampaignAudienceType { SEGMENT IMPORT MANUAL } enum CampaignType { BROADCAST BULK_FOLLOWUP } enum PaymentStatus { UNPAID PAID OVERDUE VOID } model SubscriptionPlan { id String @id @default(cuid()) name String code String @unique priceMonthly Int messageQuota Int seatQuota Int broadcastQuota Int featuresJson String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenants Tenant[] invoices BillingInvoice[] } model Tenant { id String @id @default(cuid()) name String slug String @unique companyName String timezone String status TenantStatus @default(TRIAL) planId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt plan SubscriptionPlan @relation(fields: [planId], references: [id]) users User[] roles Role[] channels Channel[] contacts Contact[] tags Tag[] conversations Conversation[] messages Message[] notes ConversationNote[] activities ConversationActivity[] segments ContactSegment[] templates MessageTemplate[] campaigns BroadcastCampaign[] auditLogs AuditLog[] webhookEvents WebhookEvent[] usageMetrics UsageMetric[] billingInvoices BillingInvoice[] } model Role { id String @id @default(cuid()) tenantId String? name String code RoleCode permissionsJson String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant? @relation(fields: [tenantId], references: [id]) users User[] } model User { id String @id @default(cuid()) tenantId String fullName String email String @unique passwordHash String roleId String status UserStatus @default(INVITED) avatarUrl String? lastLoginAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) role Role @relation(fields: [roleId], references: [id]) assignedConversations Conversation[] notes ConversationNote[] sentMessages Message[] activities ConversationActivity[] @relation("ActorActivities") campaigns BroadcastCampaign[] auditLogs AuditLog[] } model AuthToken { id String @id @default(cuid()) userId String tenantId String tokenType AuthTokenType tokenHash String @unique expiresAt DateTime consumedAt DateTime? createdByUser String? createdAt DateTime @default(now()) metadataJson String? @@index([userId, tokenType]) @@index([tenantId, tokenType, expiresAt]) } model Channel { id String @id @default(cuid()) tenantId String channelName String provider String wabaId String? phoneNumberId String? displayPhoneNumber String? status ChannelStatus @default(PENDING) webhookStatus String? lastSyncAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) contacts Contact[] conversations Conversation[] messages Message[] templates MessageTemplate[] campaigns BroadcastCampaign[] webhookEvents WebhookEvent[] } model Contact { id String @id @default(cuid()) tenantId String channelId String? externalRef String? fullName String phoneNumber String email String? avatarUrl String? countryCode String? optInStatus OptInStatus @default(UNKNOWN) lastInteractionAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) channel Channel? @relation(fields: [channelId], references: [id]) conversations Conversation[] messages Message[] contactTags ContactTag[] segmentMembers SegmentMember[] campaignRecipients CampaignRecipient[] @@unique([tenantId, phoneNumber]) } model Tag { id String @id @default(cuid()) tenantId String name String color String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) contactTags ContactTag[] conversationTags ConversationTag[] @@unique([tenantId, name]) } model ContactTag { id String @id @default(cuid()) tenantId String contactId String tagId String createdAt DateTime @default(now()) contact Contact @relation(fields: [contactId], references: [id]) tag Tag @relation(fields: [tagId], references: [id]) @@unique([contactId, tagId]) } model Conversation { id String @id @default(cuid()) tenantId String channelId String contactId String subject String? status ConversationStatus @default(OPEN) priority ConversationPriority @default(NORMAL) assignedUserId String? firstMessageAt DateTime? lastMessageAt DateTime? lastInboundAt DateTime? lastOutboundAt DateTime? resolvedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) channel Channel @relation(fields: [channelId], references: [id]) contact Contact @relation(fields: [contactId], references: [id]) assignedUser User? @relation(fields: [assignedUserId], references: [id]) messages Message[] notes ConversationNote[] activities ConversationActivity[] conversationTags ConversationTag[] @@index([tenantId, status]) @@index([tenantId, assignedUserId]) @@index([tenantId, lastMessageAt]) } model Message { id String @id @default(cuid()) tenantId String conversationId String channelId String contactId String? direction MessageDirection type MessageType @default(TEXT) providerMessageId String? @unique replyToMessageId String? contentText String? mediaUrl String? mimeType String? deliveryStatus DeliveryStatus @default(QUEUED) failedReason String? sentByUserId String? sentAt DateTime? deliveredAt DateTime? readAt DateTime? createdAt DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id]) conversation Conversation @relation(fields: [conversationId], references: [id]) channel Channel @relation(fields: [channelId], references: [id]) contact Contact? @relation(fields: [contactId], references: [id]) sentByUser User? @relation(fields: [sentByUserId], references: [id]) } model ConversationNote { id String @id @default(cuid()) tenantId String conversationId String userId String content String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) conversation Conversation @relation(fields: [conversationId], references: [id]) user User @relation(fields: [userId], references: [id]) } model ConversationTag { id String @id @default(cuid()) tenantId String conversationId String tagId String createdAt DateTime @default(now()) conversation Conversation @relation(fields: [conversationId], references: [id]) tag Tag @relation(fields: [tagId], references: [id]) @@unique([conversationId, tagId]) } model ConversationActivity { id String @id @default(cuid()) tenantId String conversationId String actorUserId String? activityType String metadataJson String? createdAt DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id]) conversation Conversation @relation(fields: [conversationId], references: [id]) actorUser User? @relation("ActorActivities", fields: [actorUserId], references: [id]) } model ContactSegment { id String @id @default(cuid()) tenantId String name String description String? rulesJson String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) members SegmentMember[] campaigns BroadcastCampaign[] } model SegmentMember { id String @id @default(cuid()) tenantId String segmentId String contactId String createdAt DateTime @default(now()) segment ContactSegment @relation(fields: [segmentId], references: [id]) contact Contact @relation(fields: [contactId], references: [id]) @@unique([segmentId, contactId]) } model MessageTemplate { id String @id @default(cuid()) tenantId String channelId String name String category String languageCode String templateType String? bodyText String headerType String? footerText String? buttonsJson String? providerTemplateId String? approvalStatus TemplateApprovalStatus @default(DRAFT) rejectedReason String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) channel Channel @relation(fields: [channelId], references: [id]) campaigns BroadcastCampaign[] } model BroadcastCampaign { id String @id @default(cuid()) tenantId String channelId String templateId String createdByUserId String name String campaignType CampaignType @default(BROADCAST) audienceType CampaignAudienceType segmentId String? scheduledAt DateTime? startedAt DateTime? finishedAt DateTime? status CampaignStatus @default(DRAFT) totalRecipients Int @default(0) totalSent Int @default(0) totalDelivered Int @default(0) totalRead Int @default(0) totalFailed Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) channel Channel @relation(fields: [channelId], references: [id]) template MessageTemplate @relation(fields: [templateId], references: [id]) createdByUser User @relation(fields: [createdByUserId], references: [id]) segment ContactSegment? @relation(fields: [segmentId], references: [id]) recipients CampaignRecipient[] @@index([tenantId, status]) @@index([status, scheduledAt]) } model CampaignRecipient { id String @id @default(cuid()) tenantId String campaignId String contactId String phoneNumber String sendStatus DeliveryStatus @default(QUEUED) sendAttempts Int @default(0) maxSendAttempts Int @default(3) lastAttemptAt DateTime? nextRetryAt DateTime? failureReason String? providerMessageId String? sentAt DateTime? deliveredAt DateTime? readAt DateTime? createdAt DateTime @default(now()) campaign BroadcastCampaign @relation(fields: [campaignId], references: [id]) contact Contact @relation(fields: [contactId], references: [id]) @@index([campaignId, sendStatus, nextRetryAt]) @@index([campaignId, sendStatus]) } model BackgroundJobState { id String @id @default(cuid()) jobName String @unique lockedBy String lockedUntil DateTime? runs Int @default(0) consecutiveFailures Int @default(0) lastRunStartedAt DateTime? lastRunCompletedAt DateTime? lastRunStatus String? lastRunSummaryJson String? lastError String? lastFailureAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model AuditLog { id String @id @default(cuid()) tenantId String actorUserId String? entityType String entityId String action String metadataJson String? ipAddress String? userAgent String? createdAt DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id]) actorUser User? @relation(fields: [actorUserId], references: [id]) } model WebhookEvent { id String @id @default(cuid()) tenantId String channelId String? eventType String providerEventId String? eventHash String? payloadJson String processStatus String failedReason String? createdAt DateTime @default(now()) processedAt DateTime? tenant Tenant @relation(fields: [tenantId], references: [id]) channel Channel? @relation(fields: [channelId], references: [id]) @@index([tenantId, channelId, eventHash]) } model UsageMetric { id String @id @default(cuid()) tenantId String metricDate DateTime inboundMessages Int @default(0) outboundMessages Int @default(0) activeContacts Int @default(0) activeAgents Int @default(0) broadcastSent Int @default(0) storageUsedMb Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) @@unique([tenantId, metricDate]) } model BillingInvoice { id String @id @default(cuid()) tenantId String planId String invoiceNumber String @unique periodStart DateTime periodEnd DateTime subtotal Int taxAmount Int totalAmount Int paymentStatus PaymentStatus @default(UNPAID) dueDate DateTime paidAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id]) plan SubscriptionPlan @relation(fields: [planId], references: [id]) }