// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema datasource db { provider = "postgresql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" previewFeatures = [] } generator zod { provider = "zod-prisma" output = "./zod" imports = "./zod-utils" relationModel = "default" } enum SchedulingType { ROUND_ROBIN @map("roundRobin") COLLECTIVE @map("collective") } enum PeriodType { UNLIMITED @map("unlimited") ROLLING @map("rolling") RANGE @map("range") } model Host { user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int eventType EventType @relation(fields: [eventTypeId], references: [id], onDelete: Cascade) eventTypeId Int isFixed Boolean @default(false) @@id([userId, eventTypeId]) } model EventType { id Int @id @default(autoincrement()) /// @zod.min(1) title String /// @zod.custom(imports.eventTypeSlug) slug String description String? position Int @default(0) /// @zod.custom(imports.eventTypeLocations) locations Json? length Int hidden Boolean @default(false) hosts Host[] users User[] @relation("user_eventtype") owner User? @relation("owner", fields: [userId], references: [id], onDelete: Cascade) userId Int? team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) teamId Int? hashedLink HashedLink? bookings Booking[] availability Availability[] webhooks Webhook[] destinationCalendar DestinationCalendar? eventName String? customInputs EventTypeCustomInput[] /// @zod.custom(imports.eventTypeBookingFields) bookingFields Json? timeZone String? periodType PeriodType @default(UNLIMITED) periodStartDate DateTime? periodEndDate DateTime? periodDays Int? periodCountCalendarDays Boolean? requiresConfirmation Boolean @default(false) /// @zod.custom(imports.recurringEventType) recurringEvent Json? disableGuests Boolean @default(false) hideCalendarNotes Boolean @default(false) minimumBookingNotice Int @default(120) beforeEventBuffer Int @default(0) afterEventBuffer Int @default(0) seatsPerTimeSlot Int? seatsShowAttendees Boolean? @default(false) schedulingType SchedulingType? schedule Schedule? @relation(fields: [scheduleId], references: [id]) scheduleId Int? // price is deprecated. It has now moved to metadata.apps.stripe.price. Plan to drop this column. price Int @default(0) // currency is deprecated. It has now moved to metadata.apps.stripe.currency. Plan to drop this column. currency String @default("usd") slotInterval Int? /// @zod.custom(imports.EventTypeMetaDataSchema) metadata Json? /// @zod.custom(imports.successRedirectUrl) successRedirectUrl String? workflows WorkflowsOnEventTypes[] /// @zod.custom(imports.bookingLimitsType) bookingLimits Json? @@unique([userId, slug]) @@unique([teamId, slug]) } model Credential { id Int @id @default(autoincrement()) // @@type is deprecated type String key Json user User? @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int? app App? @relation(fields: [appId], references: [slug], onDelete: Cascade) // How to make it a required column? appId String? destinationCalendars DestinationCalendar[] invalid Boolean? @default(false) } enum IdentityProvider { CAL GOOGLE SAML } model DestinationCalendar { id Int @id @default(autoincrement()) integration String externalId String user User? @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int? @unique booking Booking[] eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade) eventTypeId Int? @unique credentialId Int? credential Credential? @relation(fields: [credentialId], references: [id], onDelete: Cascade) } enum UserPermissionRole { USER ADMIN } model User { id Int @id @default(autoincrement()) username String? @unique name String? /// @zod.email() email String @unique emailVerified DateTime? password String? bio String? avatar String? timeZone String @default("Europe/London") weekStart String @default("Sunday") // DEPRECATED - TO BE REMOVED startTime Int @default(0) endTime Int @default(1440) // bufferTime Int @default(0) hideBranding Boolean @default(false) theme String? createdDate DateTime @default(now()) @map(name: "created") trialEndsAt DateTime? eventTypes EventType[] @relation("user_eventtype") credentials Credential[] teams Membership[] bookings Booking[] schedules Schedule[] defaultScheduleId Int? selectedCalendars SelectedCalendar[] completedOnboarding Boolean @default(false) locale String? timeFormat Int? @default(12) twoFactorSecret String? twoFactorEnabled Boolean @default(false) identityProvider IdentityProvider @default(CAL) identityProviderId String? availability Availability[] invitedTo Int? webhooks Webhook[] brandColor String @default("#292929") darkBrandColor String @default("#fafafa") // the location where the events will end up destinationCalendar DestinationCalendar? away Boolean @default(false) // participate in dynamic group booking or not allowDynamicBooking Boolean? @default(true) /// @zod.custom(imports.userMetadata) metadata Json? verified Boolean? @default(false) role UserPermissionRole @default(USER) disableImpersonation Boolean @default(false) impersonatedUsers Impersonations[] @relation("impersonated_user") impersonatedBy Impersonations[] @relation("impersonated_by_user") apiKeys ApiKey[] accounts Account[] sessions Session[] Feedback Feedback[] ownedEventTypes EventType[] @relation("owner") workflows Workflow[] routingForms App_RoutingForms_Form[] @relation("routing-form") verifiedNumbers VerifiedNumber[] hosts Host[] @@map(name: "users") } model Team { id Int @id @default(autoincrement()) /// @zod.min(1) name String /// @zod.min(1) slug String? @unique logo String? bio String? hideBranding Boolean @default(false) hideBookATeamMember Boolean @default(false) members Membership[] eventTypes EventType[] workflows Workflow[] createdAt DateTime @default(now()) /// @zod.custom(imports.teamMetadataSchema) metadata Json? theme String? brandColor String @default("#292929") darkBrandColor String @default("#fafafa") verifiedNumbers VerifiedNumber[] } enum MembershipRole { MEMBER ADMIN OWNER } model Membership { teamId Int userId Int accepted Boolean @default(false) role MembershipRole team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) disableImpersonation Boolean @default(false) @@id([userId, teamId]) } model VerificationToken { id Int @id @default(autoincrement()) identifier String token String @unique expires DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([identifier, token]) } model BookingReference { id Int @id @default(autoincrement()) /// @zod.min(1) type String /// @zod.min(1) uid String meetingId String? meetingPassword String? meetingUrl String? booking Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade) bookingId Int? externalCalendarId String? deleted Boolean? credentialId Int? } model Attendee { id Int @id @default(autoincrement()) email String name String timeZone String locale String? @default("en") booking Booking? @relation(fields: [bookingId], references: [id]) bookingId Int? } enum BookingStatus { CANCELLED @map("cancelled") ACCEPTED @map("accepted") REJECTED @map("rejected") PENDING @map("pending") } model Booking { id Int @id @default(autoincrement()) uid String @unique user User? @relation(fields: [userId], references: [id]) userId Int? references BookingReference[] eventType EventType? @relation(fields: [eventTypeId], references: [id]) eventTypeId Int? title String description String? customInputs Json? /// @zod.custom(imports.bookingResponses) responses Json? startTime DateTime endTime DateTime attendees Attendee[] location String? createdAt DateTime @default(now()) updatedAt DateTime? status BookingStatus @default(ACCEPTED) paid Boolean @default(false) payment Payment[] destinationCalendar DestinationCalendar? @relation(fields: [destinationCalendarId], references: [id]) destinationCalendarId Int? cancellationReason String? rejectionReason String? dynamicEventSlugRef String? dynamicGroupSlugRef String? rescheduled Boolean? fromReschedule String? recurringEventId String? smsReminderNumber String? workflowReminders WorkflowReminder[] scheduledJobs String[] /// @zod.custom(imports.bookingMetadataSchema) metadata Json? } model Schedule { id Int @id @default(autoincrement()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int eventType EventType[] name String timeZone String? availability Availability[] @@index([userId]) } model Availability { id Int @id @default(autoincrement()) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int? eventType EventType? @relation(fields: [eventTypeId], references: [id]) eventTypeId Int? days Int[] startTime DateTime @db.Time endTime DateTime @db.Time date DateTime? @db.Date Schedule Schedule? @relation(fields: [scheduleId], references: [id]) scheduleId Int? @@index([eventTypeId]) @@index([scheduleId]) } model SelectedCalendar { user User @relation(fields: [userId], references: [id], onDelete: Cascade) userId Int integration String externalId String @@id([userId, integration, externalId]) } enum EventTypeCustomInputType { TEXT @map("text") TEXTLONG @map("textLong") NUMBER @map("number") BOOL @map("bool") RADIO @map("radio") PHONE @map("phone") } model EventTypeCustomInput { id Int @id @default(autoincrement()) eventTypeId Int eventType EventType @relation(fields: [eventTypeId], references: [id], onDelete: Cascade) label String type EventTypeCustomInputType /// @zod.custom(imports.customInputOptionSchema) options Json? required Boolean placeholder String @default("") } model ResetPasswordRequest { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt email String expires DateTime } enum ReminderType { PENDING_BOOKING_CONFIRMATION } model ReminderMail { id Int @id @default(autoincrement()) referenceId Int reminderType ReminderType elapsedMinutes Int createdAt DateTime @default(now()) } model Payment { id Int @id @default(autoincrement()) uid String @unique app App? @relation(fields: [appId], references: [slug], onDelete: Cascade) appId String? bookingId Int booking Booking? @relation(fields: [bookingId], references: [id], onDelete: Cascade) amount Int fee Int currency String success Boolean refunded Boolean data Json externalId String @unique } enum WebhookTriggerEvents { BOOKING_CREATED BOOKING_RESCHEDULED BOOKING_CANCELLED FORM_SUBMITTED MEETING_ENDED } model Webhook { id String @id @unique userId Int? eventTypeId Int? /// @zod.url() subscriberUrl String payloadTemplate String? createdAt DateTime @default(now()) active Boolean @default(true) eventTriggers WebhookTriggerEvents[] user User? @relation(fields: [userId], references: [id], onDelete: Cascade) eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade) app App? @relation(fields: [appId], references: [slug], onDelete: Cascade) appId String? secret String? @@unique([userId, subscriberUrl], name: "courseIdentifier") } model Impersonations { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) impersonatedUser User @relation("impersonated_user", fields: [impersonatedUserId], references: [id], onDelete: Cascade) impersonatedBy User @relation("impersonated_by_user", fields: [impersonatedById], references: [id], onDelete: Cascade) impersonatedUserId Int impersonatedById Int } model ApiKey { id String @id @unique @default(cuid()) userId Int note String? createdAt DateTime @default(now()) expiresAt DateTime? lastUsedAt DateTime? hashedKey String @unique() user User? @relation(fields: [userId], references: [id], onDelete: Cascade) app App? @relation(fields: [appId], references: [slug], onDelete: Cascade) appId String? } model HashedLink { id Int @id @default(autoincrement()) link String @unique() eventType EventType @relation(fields: [eventTypeId], references: [id], onDelete: Cascade) eventTypeId Int @unique } model Account { id String @id @default(cuid()) userId Int type String provider String providerAccountId String refresh_token String? @db.Text access_token String? @db.Text expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) } model Session { id String @id @default(cuid()) sessionToken String @unique userId Int expires DateTime user User? @relation(fields: [userId], references: [id], onDelete: Cascade) } enum AppCategories { calendar messaging other payment video web3 automation analytics } model App { // The slug for the app store public page inside `/apps/[slug]` slug String @id @unique // The directory name for `/packages/app-store/[dirName]` dirName String @unique // Needed API Keys keys Json? // One or multiple categories to which this app belongs categories AppCategories[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt credentials Credential[] payments Payment[] Webhook Webhook[] ApiKey ApiKey[] enabled Boolean @default(false) } model App_RoutingForms_Form { id String @id @default(cuid()) description String? routes Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String fields Json? user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) userId Int responses App_RoutingForms_FormResponse[] disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) settings Json? } model App_RoutingForms_FormResponse { id Int @id @default(autoincrement()) formFillerId String @default(cuid()) form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) formId String response Json createdAt DateTime @default(now()) @@unique([formFillerId, formId]) } model Feedback { id Int @id @default(autoincrement()) date DateTime @default(now()) userId Int user User @relation(fields: [userId], references: [id], onDelete: Cascade) rating String comment String? } enum WorkflowTriggerEvents { BEFORE_EVENT EVENT_CANCELLED NEW_EVENT AFTER_EVENT RESCHEDULE_EVENT } enum WorkflowActions { EMAIL_HOST EMAIL_ATTENDEE SMS_ATTENDEE SMS_NUMBER EMAIL_ADDRESS } model WorkflowStep { id Int @id @default(autoincrement()) stepNumber Int action WorkflowActions workflowId Int workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) sendTo String? reminderBody String? emailSubject String? template WorkflowTemplates @default(REMINDER) workflowReminders WorkflowReminder[] numberRequired Boolean? sender String? numberVerificationPending Boolean @default(true) } model Workflow { id Int @id @default(autoincrement()) name String userId Int? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) teamId Int? activeOn WorkflowsOnEventTypes[] trigger WorkflowTriggerEvents time Int? timeUnit TimeUnit? steps WorkflowStep[] } model WorkflowsOnEventTypes { id Int @id @default(autoincrement()) workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) workflowId Int eventType EventType @relation(fields: [eventTypeId], references: [id], onDelete: Cascade) eventTypeId Int } model Deployment { /// This is a single row table, so we use a fixed id id Int @id @default(1) logo String? /// @zod.custom(imports.DeploymentTheme) theme Json? licenseKey String? agreedLicenseAt DateTime? } enum TimeUnit { DAY @map("day") HOUR @map("hour") MINUTE @map("minute") } model WorkflowReminder { id Int @id @default(autoincrement()) bookingUid String booking Booking? @relation(fields: [bookingUid], references: [uid], onDelete: Cascade) method WorkflowMethods scheduledDate DateTime referenceId String? @unique scheduled Boolean workflowStepId Int workflowStep WorkflowStep @relation(fields: [workflowStepId], references: [id], onDelete: Cascade) cancelled Boolean? } enum WorkflowTemplates { REMINDER CUSTOM } enum WorkflowMethods { EMAIL SMS } model VerifiedNumber { id Int @id @default(autoincrement()) userId Int? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) teamId Int? team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) phoneNumber String }