Prisma Models

SaasRock primary database models and relationships.

  • Alexandro Martínez
    Author
    by Alexandro Martínez
    a year ago
  • Main Models

    • User, AdminUser

    • Tenant, TenantUser, TenantSubscription

    • Role, Permission, UserRole

    • ApiKey, ApiKeyLog

    • SubscriptionProduct, SubscriptionPrice, SubscriptionFeature

    • BlogPost, BlogCategory, BlogTag

    • Entity, Property, Row, RowValue, RowMedia

    • AnalyticsUniqueVisitor, AnalyticsPageView, AnalyticsEvent

    • Page, PageBlock

    • Onboarding, OnboardingFilter, OnboardingStep, OnboardingSession

    • Workflow, WorkflowBlock, WorkflowBlockCondition, WorkflowBlockToBlock, WorkflowExecution

    • FeatureFlag, FeatureFlagFilter

    • KnowledgeBase, KnowledgeBaseCategory, KnowledgeBaseArticle

    • MetricLog

    • Credit

    • Portal, PortalUser

    Database Schema

    I'm waiting for this issue to be fixed to split schemas into module files: Support for splitting Prisma schema into multiple files #2377.

    generator client {
      provider = "prisma-client-js"
    }
    
    

    datasource db { provider = "postgresql" url = env("DATABASE_URL") // uses connection pooling }

    model AppConfiguration { id String @id @default(cuid()) updatedAt DateTime @updatedAt name String url String theme String? authRequireEmailVerification Boolean @default(false) authRequireOrganization Boolean @default(true) authRequireName Boolean @default(true) authRecaptchaSiteKey String? analyticsEnabled Boolean @default(true) analyticsSimpleAnalytics Boolean @default(false) analyticsPlausibleAnalytics Boolean @default(false) analyticsGoogleAnalyticsTrackingId String? subscriptionRequired Boolean @default(true) subscriptionAllowSubscribeBeforeSignUp Boolean @default(true) subscriptionAllowSignUpBeforeSubscribe Boolean @default(true) cookiesEnabled Boolean @default(false) metricsEnabled Boolean @default(false) metricsLogToConsole Boolean @default(false) metricsSaveToDatabase Boolean @default(false) metricsIgnoreUrls String? brandingLogo String? brandingLogoDarkMode String? brandingIcon String? brandingIconDarkMode String? brandingFavicon String? }

    model AppCookie { id String @id @default(cuid()) category Int name String description String enabled Boolean @default(true) expiry String? domain String? type String? href String? }

    model User { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt email String @unique passwordHash String firstName String lastName String avatar String? phone String? defaultTenantId String? verifyToken String? githubId String? @unique googleId String? @unique locale String? active Boolean @default(false) admin AdminUser? // Has access to the admin panel createdApiKeys ApiKey[] createdGroups Group[] groups GroupUser[] createdLinkedAccounts LinkedAccount[] logs Log[] createdRows Row[] createdRowComments RowComment[] createdRowCommentReactions RowCommentReaction[] createdEntityViews EntityView[] @relation("createdByUser") rowPermissions RowPermission[] assignedTasks RowTask[] @relation("assignedToUser") completedTasks RowTask[] @relation("completedByUser") createdRowTasks RowTask[] @relation("createdByUser") tenants TenantUser[] invitation TenantUserInvitation? @relation("createdUser") sentInvitations TenantUserInvitation[] @relation("fromUser") roles UserRole[] readEmails EmailRead[] entityViews EntityView[] onboardingSessions OnboardingSession[] tags TagUser[] tenantIpAddresses TenantIpAddress[] promptFlowExecutions PromptFlowExecution[] analyticsUniqueSessions AnalyticsUniqueVisitor[] metricLogs MetricLog[] formulaLogs FormulaLog[] createdTenantRelationships TenantRelationship[] integrationSyncs IntegrationSync[] blogArticles BlogPost[] knowledgeBaseArticles KnowledgeBaseArticle[] events Event[] workflowsCreated Workflow[] @relation("createdByUser") workflowsExecuted WorkflowExecution[] @relation("executedByUser") workflowVariablesCreated WorkflowVariable[] workflowCredentialsCreated WorkflowCredential[] feedback Feedback[] credits Credit[] portals Portal[] }

    // Has access to the admin panel model AdminUser { userId String @unique user User @relation(fields: [userId], references: [id], onDelete: Cascade) }

    model Tenant { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt slug String @unique name String icon String? subscriptionId String? active Boolean @default(false) deactivatedReason String? apiKeys ApiKey[] apiKeyLogs ApiKeyLog[] groups Group[] asClientLinkedAccounts LinkedAccount[] @relation("clientTenant") createdLinkedAccounts LinkedAccount[] @relation("createdByTenant") asProviderLinkedAccounts LinkedAccount[] @relation("providerTenant") logs Log[] rows Row[] rowPermissions RowPermission[] subscription TenantSubscription? users TenantUser[] invitations TenantUserInvitation[] userRoles UserRole[] inboundAddresses TenantInboundAddress[] events Event[] fromRegistration Registration? entityViews EntityView[] emailSenders EmailSender[] campaigns Campaign[] outboundEmails OutboundEmail[] tags TagTenant[] onboardingSessions OnboardingSession[] ipAddresses TenantIpAddress[] tenantSettingsRow TenantSettingsRow? promptFlowExecutions PromptFlowExecution[] metricLogs MetricLog[] formulaLogs FormulaLog[] types TenantType[] fromTenants TenantRelationship[] @relation("fromTenant") toTenants TenantRelationship[] @relation("toTenant") integrations Integration[] entityProperties Property[] blogPosts BlogPost[] blogTags BlogTag[] blogCategories BlogCategory[] entityGroupConfigurations EntityGroupConfiguration[] entityTemplates EntityTemplate[] workflows Workflow[] workflowExecutions WorkflowExecution[] workflowVariables WorkflowVariable[] workflowCredentials WorkflowCredential[] feedback Feedback[] credits Credit[] portals Portal[] portalsUsers PortalUser[]

    @@index([slug]) }

    model Registration { id String @id @default(cuid()) createdAt DateTime @default(now()) email String @unique firstName String lastName String slug String? token String @unique ipAddress String? company String? selectedSubscriptionPriceId String? createdTenantId String? @unique createdTenant Tenant? @relation(fields: [createdTenantId], references: [id], onDelete: Cascade) }

    model Blacklist { id String @id @default(cuid()) createdAt DateTime @default(now()) type String // email, domain, ip value String active Boolean @default(true) registerAttempts Int @default(0) }

    model TenantSubscription { id String @id @default(cuid()) tenantId String @unique stripeCustomerId String? tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) products TenantSubscriptionProduct[] }

    model TenantSubscriptionProduct { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantSubscriptionId String subscriptionProductId String cancelledAt DateTime? endsAt DateTime? stripeSubscriptionId String? quantity Int? fromCheckoutSessionId String? currentPeriodStart DateTime? currentPeriodEnd DateTime? tenantSubscription TenantSubscription @relation(fields: [tenantSubscriptionId], references: [id], onDelete: Cascade) subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id]) prices TenantSubscriptionProductPrice[] }

    model TenantSubscriptionProductPrice { id String @id @default(cuid()) tenantSubscriptionProductId String tenantSubscriptionProduct TenantSubscriptionProduct @relation(fields: [tenantSubscriptionProductId], references: [id], onDelete: Cascade) subscriptionPriceId String? subscriptionPrice SubscriptionPrice? @relation(fields: [subscriptionPriceId], references: [id]) subscriptionUsageBasedPriceId String? subscriptionUsageBasedPrice SubscriptionUsageBasedPrice? @relation(fields: [subscriptionUsageBasedPriceId], references: [id]) usageRecords TenantSubscriptionUsageRecord[] }

    model TenantSubscriptionUsageRecord { id String @id @default(cuid()) tenantSubscriptionProductPriceId String tenantSubscriptionProductPrice TenantSubscriptionProductPrice @relation(fields: [tenantSubscriptionProductPriceId], references: [id], onDelete: Cascade) timestamp Int quantity Int stripeSubscriptionItemId String? }

    model CheckoutSessionStatus { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt id String @unique pending Boolean @default(true) // has not added products to tenant email String fromUrl String fromUserId String? fromTenantId String? createdUserId String? createdTenantId String? }

    model TenantUser { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String userId String type Int joined Int status Int tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) roles TenantUserRole[] }

    model Role { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String @unique description String type String assignToNewUsers Boolean isDefault Boolean order Int permissions RolePermission[] rowPermissions RowPermission[] users UserRole[] }

    model Permission { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String @unique description String type String isDefault Boolean order Int inRoles RolePermission[] entityId String? entity Entity? @relation(fields: [entityId], references: [id], onDelete: Cascade) inTenantTypeRelationships TenantTypeRelationship[] }

    model RolePermission { id String @id @default(cuid()) roleId String permissionId String permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) }

    model UserRole { id String @id @default(cuid()) createdAt DateTime @default(now()) userId String roleId String tenantId String? role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) }

    model Group { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String tenantId String? name String description String color Int createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) users GroupUser[] rowPermissions RowPermission[]

    @@index([createdByUserId], name: "group_createdByUserId") }

    model GroupUser { id String @id @default(cuid()) groupId String userId String group Group @relation(fields: [groupId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) }

    model TenantUserRole { id String @id @default(cuid()) tenantUserId String order Int name String tenantUser TenantUser @relation(fields: [tenantUserId], references: [id], onDelete: Cascade) }

    model TenantUserInvitation { id String @id @default(cuid()) tenantId String email String firstName String lastName String type Int pending Boolean createdUserId String? @unique fromUserId String? fromUser User? @relation(name: "fromUser", fields: [fromUserId], references: [id]) user User? @relation(name: "createdUser", fields: [createdUserId], references: [id]) tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) }

    model LinkedAccount { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String createdByTenantId String providerTenantId String clientTenantId String status Int clientTenant Tenant @relation("clientTenant", fields: [clientTenantId], references: [id], onDelete: Cascade) createdByTenant Tenant @relation("createdByTenant", fields: [createdByTenantId], references: [id], onDelete: Cascade) createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) providerTenant Tenant @relation("providerTenant", fields: [providerTenantId], references: [id], onDelete: Cascade) }

    model ApiKey { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String tenantId String key String @default(uuid()) alias String expires DateTime? active Boolean createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) entities ApiKeyEntity[] apiKeyLogs ApiKeyLog[] logs Log[] createdRows Row[] tenantIpAddresses TenantIpAddress[] integrationSyncs IntegrationSync[]

    @@unique([tenantId, alias]) @@unique([tenantId, key]) @@index([key]) }

    model ApiKeyEntity { id String @id @default(cuid()) apiKeyId String entityId String create Boolean read Boolean update Boolean delete Boolean apiKey ApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) }

    model Log { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? userId String? apiKeyId String? rowId String? url String action String details String? commentId String? apiKey ApiKey? @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) comment RowComment? @relation(fields: [commentId], references: [id]) row Row? @relation(fields: [rowId], references: [id]) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) webhookLogs EntityWebhookLog[] }

    model ApiKeyLog { id String @id @default(cuid()) createdAt DateTime @default(now()) apiKeyId String? apiKey ApiKey? @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) ip String endpoint String method String params String status Int? duration Int? error String? type String?

    @@index([tenantId], name: "api_key_log_tenant") @@index([tenantId, createdAt], name: "api_key_log_tenant_created_at") @@index([tenantId, type], name: "api_key_log_tenant_type") }

    model SubscriptionProduct { id String @id @default(cuid()) stripeId String order Int title String active Boolean model Int public Boolean groupTitle String? groupDescription String? description String? badge String? billingAddressCollection String @default("auto") hasQuantity Boolean @default(false) canBuyAgain Boolean @default(false) prices SubscriptionPrice[] features SubscriptionFeature[] tenantProducts TenantSubscriptionProduct[] usageBasedPrices SubscriptionUsageBasedPrice[] assignsTenantTypes TenantType[] }

    model SubscriptionPrice { id String @id @default(cuid()) subscriptionProductId String subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade) stripeId String type Int billingPeriod Int price Decimal currency String trialDays Int active Boolean tenantProductPrices TenantSubscriptionProductPrice[] }

    model SubscriptionUsageBasedPrice { id String @id @default(cuid()) subscriptionProductId String subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade) stripeId String billingPeriod Int currency String unit String unitTitle String unitTitlePlural String usageType String aggregateUsage String tiersMode String billingScheme String tiers SubscriptionUsageBasedTier[] tenantProductPrices TenantSubscriptionProductPrice[] }

    model SubscriptionUsageBasedTier { id String @id @default(cuid()) subscriptionUsageBasedPriceId String subscriptionUsageBasedPrice SubscriptionUsageBasedPrice @relation(fields: [subscriptionUsageBasedPriceId], references: [id], onDelete: Cascade) from Int to Int? perUnitPrice Decimal? flatFeePrice Decimal? }

    model SubscriptionFeature { id String @id @default(cuid()) subscriptionProductId String order Int title String name String type Int value Int href String? badge String? accumulate Boolean @default(false) subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade) }

    model BlogCategory { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) name String color Int posts BlogPost[]

    @@unique([tenantId, name]) }

    model BlogTag { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) name String @unique color Int posts BlogPostTag[] }

    model BlogPostTag { id String @id @default(cuid()) postId String tagId String post BlogPost @relation(fields: [postId], references: [id], onDelete: Cascade) tag BlogTag @relation(fields: [tagId], references: [id], onDelete: Cascade) }

    model BlogPost { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) slug String title String description String date DateTime image String content String readingTime String published Boolean authorId String? categoryId String? contentType String @default("markdown") author User? @relation(fields: [authorId], references: [id], onDelete: Cascade) category BlogCategory? @relation(fields: [categoryId], references: [id], onDelete: Cascade) tags BlogPostTag[]

    @@unique([tenantId, slug]) }

    // <--- START: Entities --->

    // TODO: Wrap entities in modules (e.g. CRM, HelpDesk...) model Module { id String @id @default(cuid()) type String @default("app") // app, admin, all order Int name String @unique title String description String icon String entities Entity[] }

    model Entity { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt moduleId String? name String @unique slug String @unique order Int prefix String @unique type String @default("app") // app, admin, all title String titlePlural String isAutogenerated Boolean hasApi Boolean icon String active Boolean showInSidebar Boolean @default(true) hasTags Boolean @default(true) hasComments Boolean @default(true) hasTasks Boolean @default(true) hasActivity Boolean @default(true) hasBulkDelete Boolean @default(false) hasViews Boolean @default(true) defaultVisibility String @default("private") onCreated String? @default("redirectToOverview") // redirectToOverview, redirectToNew, redirectToList, redirectToEdit, addAnother onEdit String? @default("editRoute") // editRoute, overviewRoute, overviewAlwaysEditable promptFlowGroupId String? createdPermissions Permission[] apiKeys ApiKeyEntity[] tags EntityTag[] permissions EntityTenantUserPermission[] webhooks EntityWebhook[] properties Property[] rows Row[] views EntityView[] parentEntities EntityRelationship[] @relation(name: "childEntities") childEntities EntityRelationship[] @relation(name: "parentEntities") module Module? @relation(fields: [moduleId], references: [id]) groups EntityGroupEntity[] promptFlowGroup PromptFlowGroupEntity[] inTenantTypes TenantTypeEntity[] promptFlows PromptFlow[] inPromptFlowOutputs PromptFlowOutput[] integrationObjectMappings IntegrationObjectMap[] integrationSyncLogs IntegrationSyncLog[] templates EntityTemplate[]

    @@index([name], name: "entity_name") @@index([slug], name: "entity_slug") }

    model Property { id String @id @default(cuid()) entityId String order Int name String title String type Int subtype String? isDefault Boolean @default(false) isRequired Boolean @default(false) isHidden Boolean @default(false) isDisplay Boolean @default(false) isUnique Boolean @default(false) // TODO: Validate uniqueness isReadOnly Boolean @default(false) showInCreate Boolean @default(true) canUpdate Boolean @default(true) formulaId String? formula Formula? @relation(fields: [formulaId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id]) attributes PropertyAttribute[] entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) options PropertyOption[] values RowValue[] inViewProperties EntityViewProperty[] inViewGroupBy EntityView[] inPromptFlowMappings PromptFlowOutputMapping[] integrationPropertyMappings IntegrationPropertyMap[]

    @@unique([entityId, name, tenantId]) @@unique([entityId, title, tenantId]) @@index([entityId], name: "entity_property") @@index([entityId, name], name: "entity_property_name") }

    model EntityView { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdByUserId String? createdByUser User? @relation(name: "createdByUser", fields: [createdByUserId], references: [id]) entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) userId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) layout String @default("table") // table, board, calendar, list, gallery... order Int name String title String pageSize Int isDefault Boolean isSystem Boolean @default(false) gridColumns Int? @default(5) gridColumnsSm Int? @default(2) gridColumnsMd Int? @default(3) gridColumnsLg Int? @default(4) gridColumnsXl Int? @default(5) gridColumns2xl Int? @default(6) gridGap String? @default("sm") properties EntityViewProperty[] filters EntityViewFilter[] sort EntityViewSort[] groupByPropertyId String? groupByProperty Property? @relation(fields: [groupByPropertyId], references: [id], onDelete: Cascade) inGroups EntityGroupEntity[] inChildRelationships EntityRelationship[] @relation(name: "childEntityView") inParentRelationships EntityRelationship[] @relation(name: "parentEntityView")

    @@index([entityId], name: "entity_view") @@index([entityId, name], name: "entity_view_name") }

    model EntityViewProperty { id String @id @default(cuid()) entityViewId String entityView EntityView @relation(fields: [entityViewId], references: [id], onDelete: Cascade) propertyId String? property Property? @relation(fields: [propertyId], references: [id], onDelete: Cascade) name String? // if not a property, e.g. "default.folio" order Int

    @@index([entityViewId], name: "entity_view_property") @@index([entityViewId, name], name: "entity_view_property_name") }

    model EntityViewFilter { id String @id @default(cuid()) entityViewId String entityView EntityView @relation(fields: [entityViewId], references: [id], onDelete: Cascade) match String @default("and") // and, or name String condition String // is, isNot, contains, doesNotContain... value String

    @@index([entityViewId], name: "entity_view_filter") @@index([entityViewId, name], name: "entity_view_filter_name") }

    model EntityViewSort { id String @id @default(cuid()) entityViewId String entityView EntityView @relation(fields: [entityViewId], references: [id], onDelete: Cascade) name String asc Boolean order Int

    @@index([entityViewId], name: "entity_view_sort") @@index([entityViewId, name], name: "entity_view_sort_name") }

    model PropertyAttribute { id String @id @default(cuid()) propertyId String property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade) name String // pattern, min, max, step, rows, defaultValue, maxSize, acceptFileTypes, uppercase... value String

    @@unique([propertyId, name]) @@index([propertyId], name: "property_attribute") @@index([propertyId, name], name: "property_attribute_name") }

    model PropertyOption { id String @id @default(cuid()) propertyId String order Int value String name String? color Int @default(0) property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade) // values RowValueSelection[]

    @@index([propertyId], name: "property_option") @@index([propertyId, name], name: "property_option_name") }

    model EntityTag { id String @id @default(cuid()) createdAt DateTime @default(now()) entityId String value String color Int entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) rowTags RowTag[]

    @@index([entityId], name: "entity_tag") @@index([entityId, value], name: "entity_tag_value") }

    model EntityTenantUserPermission { id String @id @default(cuid()) entityId String level Int entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) }

    model EntityWebhook { id String @id @default(cuid()) entityId String action String method String endpoint String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) logs EntityWebhookLog[] }

    model EntityWebhookLog { id String @id @default(cuid()) webhookId String logId String status Int error String? log Log @relation(fields: [logId], references: [id], onDelete: Cascade) webhook EntityWebhook @relation(fields: [webhookId], references: [id], onDelete: Cascade) }

    model EntityRelationship { id String @id @default(cuid()) parentId String parent Entity @relation(name: "parentEntities", fields: [parentId], references: [id], onDelete: Cascade) childId String child Entity @relation(name: "childEntities", fields: [childId], references: [id], onDelete: Cascade) order Int? title String? type String @default("one-to-many") required Boolean @default(false) cascade Boolean @default(false) readOnly Boolean @default(false) hiddenIfEmpty Boolean @default(false) childEntityViewId String? childEntityView EntityView? @relation(name: "childEntityView", fields: [childEntityViewId], references: [id], onDelete: Cascade) parentEntityViewId String? parentEntityView EntityView? @relation(name: "parentEntityView", fields: [parentEntityViewId], references: [id], onDelete: Cascade) rows RowRelationship[]

    @@unique([parentId, childId, title]) @@index([parentId], name: "parent_entity_relationship") @@index([childId], name: "child_entity_relationship") @@index([parentId, childId], name: "parent_child_entity_relationship") @@index([parentId, childId, order], name: "parent_child_entity_relationship_order") }

    model SampleCustomEntity { rowId String @unique row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) customText String customNumber Decimal customDate DateTime customBoolean Boolean customSelect String }

    model RowRelationship { id String @id @default(cuid()) createdAt DateTime @default(now()) relationshipId String relationship EntityRelationship @relation(fields: [relationshipId], references: [id], onDelete: Cascade) parentId String parent Row @relation(name: "parentRow", fields: [parentId], references: [id], onDelete: Cascade) childId String child Row @relation(name: "childRow", fields: [childId], references: [id], onDelete: Cascade) metadata String? integrationSyncLogs IntegrationSyncLog[]

    @@unique([parentId, childId]) @@index([parentId], name: "parent_row_relationship") @@index([childId], name: "child_row_relationship") @@index([parentId, childId], name: "parent_child_row_relationship") }

    model Row { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? entityId String tenantId String? folio Int createdByUserId String? createdByApiKeyId String? order Int @default(0) createdByApiKey ApiKey? @relation(fields: [createdByApiKeyId], references: [id]) createdByUser User? @relation(fields: [createdByUserId], references: [id]) entity Entity @relation(fields: [entityId], references: [id]) tenant Tenant? @relation(fields: [tenantId], references: [id]) logs Log[] comments RowComment[] permissions RowPermission[] tags RowTag[] tasks RowTask[] values RowValue[] childRows RowRelationship[] @relation("parentRow") parentRows RowRelationship[] @relation("childRow") sampleCustomEntity SampleCustomEntity? outboundEmails OutboundEmail[] tenantSettingsRow TenantSettingsRow[] formulaCalculationLogs FormulaComponentLog[] integrationSyncLogs IntegrationSyncLog[] integrationRows IntegrationRow[] inEntityGroupConfigurationRows EntityGroupConfigurationRow[]

    @@index([deletedAt], name: "row_deletedAt") @@index([entityId], name: "row_entity") @@index([entityId, tenantId], name: "row_entity_tenant") @@index([entityId, tenantId, createdAt], name: "row_entity_tenant_created_at") @@index([tenantId], name: "row_tenant") @@index([createdByUserId], name: "row_createdByUserId") }

    model RowValue { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt rowId String propertyId String textValue String? numberValue Decimal? dateValue DateTime? booleanValue Boolean? // selection RowValueSelection[] // TODO: MULTI SELECT property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) media RowMedia[] multiple RowValueMultiple[] range RowValueRange?

    @@index([rowId], name: "row_value_row") @@index([rowId, propertyId], name: "row_value_row_property") }

    model RowValueMultiple { id String @id @default(cuid()) rowValueId String order Int value String rowValue RowValue @relation(fields: [rowValueId], references: [id], onDelete: Cascade)

    @@index([rowValueId], name: "row_value_multiple_row_value") }

    model RowValueRange { rowValueId String @unique numberMin Decimal? numberMax Decimal? dateMin DateTime? dateMax DateTime? rowValue RowValue @relation(fields: [rowValueId], references: [id], onDelete: Cascade)

    @@index([rowValueId], name: "row_value_range_row_value") }

    // // TODO: MULTI SELECT // model RowValueSelection { // id String @id @default(cuid()) // rowValueId String // propertyOptionId String // propertyOption PropertyOption @relation(fields: [propertyOptionId], references: [id], onDelete: Cascade) // rowValue RowValue @relation(fields: [rowValueId], references: [id], onDelete: Cascade) // }

    model RowPermission { id String @id @default(cuid()) rowId String tenantId String? roleId String? groupId String? userId String? public Boolean? access String @default("view") group Group? @relation(fields: [groupId], references: [id], onDelete: Cascade) role Role? @relation(fields: [roleId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade)

    @@index([rowId], name: "row_permission_row") @@index([rowId, tenantId], name: "row_permission_row_tenant") @@index([rowId, roleId], name: "row_permission_row_role") @@index([rowId, groupId], name: "row_permission_row_group") @@index([rowId, userId], name: "row_permission_row_user") @@index([public], name: "row_permission_public") }

    model RowMedia { id String @id @default(cuid()) rowValueId String title String name String file String type String publicUrl String? storageBucket String? storageProvider String? rowValue RowValue @relation(fields: [rowValueId], references: [id], onDelete: Cascade)

    @@index([rowValueId], name: "row_media_row_value") @@index([rowValueId, name], name: "row_media_row_value_name") }

    model RowTag { id String @id @default(cuid()) createdAt DateTime @default(now()) rowId String tagId String row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) tag EntityTag @relation(fields: [tagId], references: [id], onDelete: Cascade)

    @@index([rowId], name: "row_tag_row") @@index([rowId, tagId], name: "row_tag_row_tag") }

    model RowComment { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String rowId String value String isDeleted Boolean? createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) logs Log[] reactions RowCommentReaction[]

    @@index([rowId], name: "row_comment_row") }

    model RowCommentReaction { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String rowCommentId String reaction String createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) rowComment RowComment @relation(fields: [rowCommentId], references: [id], onDelete: Cascade) }

    model RowTask { id String @id @default(cuid()) createdAt DateTime @default(now()) createdByUserId String rowId String title String description String completed Boolean completedAt DateTime? completedByUserId String? assignedToUserId String? deadline DateTime? assignedToUser User? @relation("assignedToUser", fields: [assignedToUserId], references: [id], onDelete: Cascade) completedByUser User? @relation("completedByUser", fields: [completedByUserId], references: [id], onDelete: Cascade) createdByUser User @relation("createdByUser", fields: [createdByUserId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) }

    model EntityTemplate { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) title String config String

    @@unique([tenantId, entityId, title]) }

    // <--- END: Entities --->

    // <--- START: Inbound Emails --->

    model TenantInboundAddress { id String @id @default(cuid()) tenantId String tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) address String @unique email Email[] }

    model Email { id String @id @default(cuid()) tenantInboundAddressId String? tenantInboundAddress TenantInboundAddress? @relation(fields: [tenantInboundAddressId], references: [id], onDelete: Cascade) messageId String @unique type String // inbound, outbound date DateTime subject String fromEmail String fromName String? toEmail String toName String? textBody String htmlBody String reads EmailRead[] attachments EmailAttachment[] cc EmailCc[] }

    model EmailRead { id String @id @default(cuid()) createdAt DateTime @default(now()) emailId String email Email @relation(fields: [emailId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) }

    model EmailCc { id String @id @default(cuid()) emailId String toEmail String toName String? email Email @relation(fields: [emailId], references: [id], onDelete: Cascade) }

    model EmailAttachment { id String @id @default(cuid()) emailId String name String type String length Int content String publicUrl String? storageBucket String? storageProvider String? email Email @relation(fields: [emailId], references: [id], onDelete: Cascade) }

    // <--- END: Inbound Emails --->

    // <--- START: Events --->

    model Event { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id]) userId String? user User? @relation(fields: [userId], references: [id]) name String data String resource String? description String? attempts EventWebhookAttempt[] }

    model EventWebhookAttempt { id String @id @default(cuid()) createdAt DateTime @default(now()) startedAt DateTime? finishedAt DateTime? eventId String event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) endpoint String success Boolean? status Int? message String? body String? }

    // <--- END: Events --->

    // <!--- Analytics --->

    model AnalyticsSettings { id String @id @default(cuid()) public Boolean @default(false) ignorePages String onlyPages String }

    model AnalyticsUniqueVisitor { id String @id @default(cuid()) createdAt DateTime @default(now()) cookie String @unique via String? httpReferrer String? browser String? browserVersion String? os String? osVersion String? device String? source String? medium String? campaign String? content String? term String? country String? city String? fromUrl String? fromRoute String? pageViews AnalyticsPageView[] events AnalyticsEvent[] userId String? user User? @relation(fields: [userId], references: [id]) portalId String? portal Portal? @relation(fields: [portalId], references: [id], onDelete: Cascade) }

    model AnalyticsPageView { id String @id @default(cuid()) createdAt DateTime @default(now()) uniqueVisitorId String uniqueVisitor AnalyticsUniqueVisitor @relation(fields: [uniqueVisitorId], references: [id], onDelete: Cascade) url String route String? portalId String? portal Portal? @relation(fields: [portalId], references: [id], onDelete: Cascade) }

    model AnalyticsEvent { id String @id @default(cuid()) createdAt DateTime @default(now()) uniqueVisitorId String uniqueVisitor AnalyticsUniqueVisitor @relation(fields: [uniqueVisitorId], references: [id], onDelete: Cascade) action String category String? label String? value String? url String? route String? featureFlagId String? featureFlag FeatureFlag? @relation(fields: [featureFlagId], references: [id]) portalId String? portal Portal? @relation(fields: [portalId], references: [id], onDelete: Cascade) }

    // <!--- Email marketing --->

    model EmailSender { id String @id @default(cuid()) tenantId String? provider String stream String apiKey String fromEmail String fromName String? replyToEmail String? tenant Tenant? @relation(fields: [tenantId], references: [id]) campaigns Campaign[] outboundEmails OutboundEmail[] }

    model Campaign { id String @id @default(cuid()) tenantId String? emailSenderId String name String subject String htmlBody String textBody String? status String @default("draft") track Boolean sentAt DateTime? emailSender EmailSender @relation(fields: [emailSenderId], references: [id]) recipients OutboundEmail[] tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) }

    model OutboundEmail { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) campaignId String? campaign Campaign? @relation(fields: [campaignId], references: [id], onDelete: Cascade) contactRowId String? contactRow Row? @relation(fields: [contactRowId], references: [id]) email String fromSenderId String isPreview Boolean? error String? sentAt DateTime? deliveredAt DateTime? bouncedAt DateTime? spamComplainedAt DateTime? unsubscribedAt DateTime? fromSender EmailSender @relation(fields: [fromSenderId], references: [id]) opens OutboundEmailOpen[] clicks OutboundEmailClick[] }

    model OutboundEmailOpen { id String @id @default(cuid()) createdAt DateTime @default(now()) firstOpen Boolean outboundEmailId String outboundEmail OutboundEmail @relation(fields: [outboundEmailId], references: [id], onDelete: Cascade) }

    model OutboundEmailClick { id String @id @default(cuid()) createdAt DateTime @default(now()) link String outboundEmailId String outboundEmail OutboundEmail @relation(fields: [outboundEmailId], references: [id], onDelete: Cascade) }

    model Page { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt slug String isPublished Boolean @default(false) isPublic Boolean @default(false) metaTags PageMetaTag[] blocks PageBlock[] }

    model PageMetaTag { id String @id @default(cuid()) pageId String? page Page? @relation(fields: [pageId], references: [id], onDelete: Cascade) order Int? name String value String

    @@unique([pageId, name, value]) }

    model PageBlock { id String @id @default(cuid()) pageId String page Page @relation(fields: [pageId], references: [id]) order Int type String // hero, gallery, logoClouds, video... value String }

    model Tag { id String @id @default(cuid()) name String @unique color Int? users TagUser[] tenants TagTenant[] }

    model TagUser { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id]) tagId String tag Tag @relation(fields: [tagId], references: [id]) }

    model TagTenant { id String @id @default(cuid()) tenantId String tenant Tenant @relation(fields: [tenantId], references: [id]) tagId String tag Tag @relation(fields: [tagId], references: [id]) }

    model Onboarding { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt title String type String // modal, page realtime Boolean @default(false) active Boolean @default(false) canBeDismissed Boolean @default(true) height String? filters OnboardingFilter[] steps OnboardingStep[] sessions OnboardingSession[] }

    model OnboardingFilter { id String @id @default(cuid()) createdAt DateTime @default(now()) onboardingId String onboarding Onboarding @relation(fields: [onboardingId], references: [id], onDelete: Cascade) type String value String? matches OnboardingSessionFilterMatch[] }

    model OnboardingStep { id String @id @default(cuid()) onboardingId String onboarding Onboarding @relation(fields: [onboardingId], references: [id], onDelete: Cascade) order Int block String sessionSteps OnboardingSessionStep[] }

    model OnboardingSession { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt onboardingId String onboarding Onboarding @relation(fields: [onboardingId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) status String // active, completed, dismissed startedAt DateTime? completedAt DateTime? dismissedAt DateTime? createdRealtime Boolean @default(false) matches OnboardingSessionFilterMatch[] sessionSteps OnboardingSessionStep[] actions OnboardingSessionAction[]

    @@unique([onboardingId, userId, tenantId]) }

    model OnboardingSessionAction { id String @id @default(cuid()) createdAt DateTime @default(now()) onboardingSessionId String onboardingSession OnboardingSession @relation(fields: [onboardingSessionId], references: [id], onDelete: Cascade) type String name String value String }

    model OnboardingSessionFilterMatch { id String @id @default(cuid()) onboardingFilterId String onboardingFilter OnboardingFilter @relation(fields: [onboardingFilterId], references: [id], onDelete: Cascade) onboardingSessionId String onboardingSession OnboardingSession @relation(fields: [onboardingSessionId], references: [id], onDelete: Cascade) }

    model OnboardingSessionStep { id String @id @default(cuid()) onboardingSessionId String onboardingSession OnboardingSession @relation(fields: [onboardingSessionId], references: [id], onDelete: Cascade) stepId String step OnboardingStep @relation(fields: [stepId], references: [id], onDelete: Cascade) seenAt DateTime? completedAt DateTime? }

    model TenantIpAddress { id String @id @default(cuid()) tenantId String tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) userId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) apiKeyId String? apiKey ApiKey? @relation(fields: [apiKeyId], references: [id], onDelete: Cascade) ip String fromUrl String createdAt DateTime @default(now())

    @@unique([tenantId, ip, userId, apiKeyId]) }

    model FeatureFlag { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String description String enabled Boolean @default(false) events AnalyticsEvent[] filters FeatureFlagFilter[]

    @@unique([name, description]) }

    model FeatureFlagFilter { id String @id @default(cuid()) createdAt DateTime @default(now()) featureFlagId String featureFlag FeatureFlag @relation(fields: [featureFlagId], references: [id], onDelete: Cascade) type String // [FeatureFlagsFilterType] percentage, darkMode, language.is, page.is, analytics.browser.is... value String? // 0-100, true, false, en, de, fr, ... action String? }

    model TenantSettingsRow { tenantId String @unique rowId String tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) row Row @relation(fields: [rowId], references: [id], onDelete: Cascade) }

    model PromptFlowGroup { id String @id @default(cuid()) createdAt DateTime @default(now()) order Int title String description String templates PromptFlowGroupTemplate[] promptFlows PromptFlow[] entities PromptFlowGroupEntity[] }

    model PromptFlowGroupTemplate { id String @id @default(cuid()) createdAt DateTime @default(now()) order Int title String promptFlowGroupId String promptFlowGroup PromptFlowGroup @relation(fields: [promptFlowGroupId], references: [id], onDelete: Cascade) }

    model PromptFlowGroupEntity { entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) promptFlowGroupId String promptFlowGroup PromptFlowGroup @relation(fields: [promptFlowGroupId], references: [id], onDelete: Cascade)

    @@id([entityId, promptFlowGroupId]) }

    model PromptFlow { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt model String // gpt-3.5-turbo, gpt-4... title String description String actionTitle String? executionType String @default("sequential") // sequential, parallel promptFlowGroupId String? stream Boolean @default(false) public Boolean @default(true) inputVariables PromptFlowInputVariable[] inputEntityId String? inputEntity Entity? @relation(fields: [inputEntityId], references: [id], onDelete: Cascade) promptFlowGroup PromptFlowGroup? @relation(fields: [promptFlowGroupId], references: [id], onDelete: Cascade) templates PromptTemplate[] executions PromptFlowExecution[] outputs PromptFlowOutput[] }

    model PromptFlowInputVariable { id String @id @default(cuid()) promptFlowId String promptFlow PromptFlow @relation(fields: [promptFlowId], references: [id], onDelete: Cascade) type String name String title String isRequired Boolean }

    model PromptTemplate { id String @id @default(cuid()) flowId String flow PromptFlow @relation(fields: [flowId], references: [id], onDelete: Cascade) order Int title String template String temperature Decimal maxTokens Int? generations Int? results PromptTemplateResult[] inPromptFlowMappings PromptFlowOutputMapping[] }

    model PromptFlowOutput { id String @id @default(cuid()) promptFlowId String promptFlow PromptFlow @relation(fields: [promptFlowId], references: [id], onDelete: Cascade) type String entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) mappings PromptFlowOutputMapping[] }

    model PromptFlowOutputMapping { id String @id @default(cuid()) promptFlowOutputId String promptFlowOutput PromptFlowOutput @relation(fields: [promptFlowOutputId], references: [id], onDelete: Cascade) promptTemplateId String promptTemplate PromptTemplate @relation(fields: [promptTemplateId], references: [id], onDelete: Cascade) propertyId String property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)

    @@unique([promptFlowOutputId, promptTemplateId, propertyId]) }

    model PromptFlowExecution { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt flowId String flow PromptFlow @relation(fields: [flowId], references: [id], onDelete: Cascade) model String? userId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) status String // pending, running, success, error error String? startedAt DateTime? completedAt DateTime? duration Int? results PromptTemplateResult[] }

    model PromptTemplateResult { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt flowExecutionId String flowExecution PromptFlowExecution @relation(fields: [flowExecutionId], references: [id]) templateId String? template PromptTemplate? @relation(fields: [templateId], references: [id]) order Int status String // pending, running, success, error prompt String response String? error String? startedAt DateTime? completedAt DateTime? }

    model MetricLog { id String @id @default(cuid()) createdAt DateTime @default(now()) env String type String // loader, action route String url String function String duration Int userId String? tenantId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) }

    model Formula { id String @id @default(cuid()) createdAt DateTime @default(now()) name String description String? resultAs String // string, number, boolean, date calculationTrigger String // one or many: always, onList, onCreated, onOverview, onEdit, custom, manual withLogs Boolean @default(false) components FormulaComponent[] inProperties Property[] logs FormulaLog[] }

    model FormulaComponent { id String @id @default(cuid()) formulaId String formula Formula @relation(fields: [formulaId], references: [id], onDelete: Cascade) order Int type String //name, operator, parenthesis, value value String }

    model FormulaLog { id String @id @default(cuid()) createdAt DateTime @default(now()) formulaId String userId String? tenantId String? originalTrigger String? triggeredBy String expression String result String duration Int @default(0) error String? rowValueId String? formula Formula @relation(fields: [formulaId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) components FormulaComponentLog[] }

    model FormulaComponentLog { id String @id @default(cuid()) order Int type String //name, operator, parenthesis, value value String rowId String? row Row? @relation(fields: [rowId], references: [id], onDelete: Cascade) formulaLogId String formulaLog FormulaLog @relation(fields: [formulaLogId], references: [id], onDelete: Cascade) }

    model EntityGroup { id String @id @default(cuid()) createdAt DateTime @default(now()) order Int slug String title String icon String collapsible Boolean section String? entities EntityGroupEntity[] configurationRows EntityGroupConfiguration[]

    @@unique([slug]) }

    model EntityGroupEntity { id String @id @default(cuid()) entityGroupId String entityGroup EntityGroup @relation(fields: [entityGroupId], references: [id], onDelete: Cascade) entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) allViewId String? allView EntityView? @relation(fields: [allViewId], references: [id], onDelete: Cascade) selectMin Int? selectMax Int? }

    model TenantType { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt title String @unique titlePlural String description String? isDefault Boolean @default(false) tenants Tenant[] fromTypes TenantTypeRelationship[] @relation("fromType") toTypes TenantTypeRelationship[] @relation("toType") subscriptionProducts SubscriptionProduct[] entities TenantTypeEntity[] }

    model TenantTypeEntity { id String @id @default(cuid()) tenantTypeId String? tenantType TenantType? @relation(fields: [tenantTypeId], references: [id], onDelete: Cascade) entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) enabled Boolean }

    model TenantTypeRelationship { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt canCreate Boolean @default(false) fromTypeId String? toTypeId String? fromType TenantType? @relation(name: "fromType", fields: [fromTypeId], references: [id], onDelete: Cascade) toType TenantType? @relation(name: "toType", fields: [toTypeId], references: [id], onDelete: Cascade) permissions Permission[] relationships TenantRelationship[]

    @@unique([fromTypeId, toTypeId]) }

    model TenantRelationship { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantTypeRelationshipId String fromTenantId String toTenantId String createdByUserId String? tenantTypeRelationship TenantTypeRelationship @relation(fields: [tenantTypeRelationshipId], references: [id], onDelete: Cascade) fromTenant Tenant @relation(name: "fromTenant", fields: [fromTenantId], references: [id], onDelete: Cascade) toTenant Tenant @relation(name: "toTenant", fields: [toTenantId], references: [id], onDelete: Cascade) createdByUser User? @relation(fields: [createdByUserId], references: [id], onDelete: Cascade)

    @@unique([fromTenantId, toTenantId]) }

    model Integration { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt type String config String tenantId String? isAdmin Boolean @default(false) tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) objectMappings IntegrationObjectMap[] syncs IntegrationSync[]

    @@unique([type, isAdmin, tenantId]) }

    model IntegrationObjectMap { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt integrationId String integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade) fromName String fromTitle String toEntityId String toEntity Entity @relation(fields: [toEntityId], references: [id], onDelete: Cascade) propertyMappings IntegrationPropertyMap[] rows IntegrationRow[] }

    model IntegrationPropertyMap { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt objectMapId String objectMap IntegrationObjectMap @relation(fields: [objectMapId], references: [id], onDelete: Cascade) fromName String fromTitle String fromType String type String // default, custom isUnique Boolean @default(false) toPropertyId String toProperty Property @relation(fields: [toPropertyId], references: [id], onDelete: Cascade) }

    model IntegrationSync { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt integrationId String integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade) startedAt DateTime? completedAt DateTime? duration Int? status String error String? createdByUserId String? createdByUser User? @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) createdByApiKeyId String? createdByApiKey ApiKey? @relation(fields: [createdByApiKeyId], references: [id], onDelete: Cascade) logs IntegrationSyncLog[] }

    model IntegrationSyncLog { id String @id @default(cuid()) createdAt DateTime @default(now()) syncId String sync IntegrationSync @relation(fields: [syncId], references: [id], onDelete: Cascade) type String operation String details String entityId String entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade) rowId String? row Row? @relation(fields: [rowId], references: [id]) relationshipId String? relationship RowRelationship? @relation(fields: [relationshipId], references: [id]) }

    model IntegrationRow { id String @id @default(cuid()) objectMapId String objectMap IntegrationObjectMap @relation(fields: [objectMapId], references: [id], onDelete: Cascade) rowId String row Row @relation(fields: [rowId], references: [id]) externalId String }

    model KnowledgeBase { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt basePath String slug String title String description String defaultLanguage String layout String color Int enabled Boolean languages String links String logo String seoImage String categories KnowledgeBaseCategory[] articles KnowledgeBaseArticle[] views KnowledgeBaseViews[]

    @@unique([slug]) }

    model KnowledgeBaseCategory { id String @id @default(cuid()) knowledgeBaseId String knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id]) slug String order Int title String description String icon String language String seoImage String sections KnowledgeBaseCategorySection[] articles KnowledgeBaseArticle[]

    @@unique([knowledgeBaseId, slug]) }

    model KnowledgeBaseCategorySection { id String @id @default(cuid()) categoryId String category KnowledgeBaseCategory @relation(fields: [categoryId], references: [id], onDelete: Cascade) order Int title String description String articles KnowledgeBaseArticle[] }

    model KnowledgeBaseArticle { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt knowledgeBaseId String knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id]) categoryId String? category KnowledgeBaseCategory? @relation(fields: [categoryId], references: [id]) sectionId String? section KnowledgeBaseCategorySection? @relation(fields: [sectionId], references: [id]) slug String title String description String order Int contentDraft String contentPublished String @default("") contentPublishedAsText String @default("") contentType String language String featuredOrder Int? seoImage String publishedAt DateTime? relatedInArticleId String? createdByUserId String? createdByUser User? @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) views KnowledgeBaseArticleViews[] upvotes KnowledgeBaseArticleUpvotes[] downvotes KnowledgeBaseArticleDownvotes[] relatedArticles KnowledgeBaseRelatedArticle[] @relation(name: "relatedArticles") relatedInArticles KnowledgeBaseRelatedArticle[] @relation(name: "relatedInArticles")

    @@unique([knowledgeBaseId, slug]) }

    model KnowledgeBaseRelatedArticle { id String @id @default(cuid()) articleId String article KnowledgeBaseArticle @relation(name: "relatedArticles", fields: [articleId], references: [id], onDelete: Cascade) relatedArticle KnowledgeBaseArticle @relation(name: "relatedInArticles", fields: [relatedArticleId], references: [id], onDelete: Cascade) relatedArticleId String }

    model KnowledgeBaseViews { id String @id @default(cuid()) createdAt DateTime @default(now()) knowledgeBaseId String knowledgeBase KnowledgeBase @relation(fields: [knowledgeBaseId], references: [id], onDelete: Cascade) userAnalyticsId String

    @@unique([knowledgeBaseId, userAnalyticsId]) }

    model KnowledgeBaseArticleViews { knowledgeBaseArticleId String knowledgeBaseArticle KnowledgeBaseArticle @relation(fields: [knowledgeBaseArticleId], references: [id], onDelete: Cascade) userAnalyticsId String

    @@unique([knowledgeBaseArticleId, userAnalyticsId]) }

    model KnowledgeBaseArticleUpvotes { createdAt DateTime @default(now()) knowledgeBaseArticleId String knowledgeBaseArticle KnowledgeBaseArticle @relation(fields: [knowledgeBaseArticleId], references: [id], onDelete: Cascade) userAnalyticsId String

    @@unique([knowledgeBaseArticleId, userAnalyticsId]) }

    model KnowledgeBaseArticleDownvotes { createdAt DateTime @default(now()) knowledgeBaseArticleId String knowledgeBaseArticle KnowledgeBaseArticle @relation(fields: [knowledgeBaseArticleId], references: [id], onDelete: Cascade) userAnalyticsId String

    @@unique([knowledgeBaseArticleId, userAnalyticsId]) }

    model EntityGroupConfiguration { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) entityGroupId String entityGroup EntityGroup @relation(fields: [entityGroupId], references: [id], onDelete: Cascade) title String rows EntityGroupConfigurationRow[] }

    model EntityGroupConfigurationRow { id String @id @default(cuid()) entityGroupConfigurationId String entityGroupConfiguration EntityGroupConfiguration @relation(fields: [entityGroupConfigurationId], references: [id], onDelete: Cascade) rowId String row Row @relation(fields: [rowId], references: [id], onDelete: Cascade)

    @@unique([entityGroupConfigurationId, rowId]) }

    model FileUploadProgress { id String @id @default(cuid()) fileName String progressServer Int progressStorage Int url String? error String? chunks FileChunk[] }

    model FileChunk { id Int @id @default(autoincrement()) fileUploadId String fileUpload FileUploadProgress @relation(fields: [fileUploadId], references: [id], onDelete: Cascade) index Int data Bytes }

    model Workflow { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String description String status String @default("draft") tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id]) createdByUserId String? createdByUser User? @relation(name: "createdByUser", fields: [createdByUserId], references: [id]) appliesToAllTenants Boolean @default(false) blocks WorkflowBlock[] inputExamples WorkflowInputExample[] executions WorkflowExecution[] }

    model WorkflowBlock { id String @id @default(cuid()) workflowId String // Foreign key to the Workflow workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt type String description String isTrigger Boolean @default(false) isBlock Boolean @default(false) fromBlocks WorkflowBlockToBlock[] @relation(name: "toBlock") toBlocks WorkflowBlockToBlock[] @relation(name: "fromBlock") input String executions WorkflowBlockExecution[] @relation(name: "executions") fromExecutions WorkflowBlockExecution[] @relation(name: "fromExecutions") conditionsGroups WorkflowBlockConditionGroup[] waitingInExecutions WorkflowExecution[] }

    model WorkflowBlockConditionGroup { id String @id @default(cuid()) workflowBlockId String workflowBlock WorkflowBlock @relation(fields: [workflowBlockId], references: [id], onDelete: Cascade) index Int type String // AND or OR conditions WorkflowBlockCondition[] }

    model WorkflowBlockCondition { id String @id @default(cuid()) workflowBlockConditionGroupId String workflowBlockConditionGroup WorkflowBlockConditionGroup @relation(fields: [workflowBlockConditionGroupId], references: [id], onDelete: Cascade) index Int variable String operator String value String }

    model WorkflowBlockToBlock { id String @id @default(cuid()) fromBlockId String fromBlock WorkflowBlock @relation(name: "fromBlock", fields: [fromBlockId], references: [id], onDelete: Cascade) toBlockId String toBlock WorkflowBlock @relation(name: "toBlock", fields: [toBlockId], references: [id], onDelete: Cascade) condition String?

    @@unique([fromBlockId, toBlockId]) }

    model WorkflowExecution { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt workflowId String workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) type String // manual, api, stream status String input String output String? duration Int? endedAt DateTime? error String? waitingBlockId String? waitingBlock WorkflowBlock? @relation(fields: [waitingBlockId], references: [id]) createdByUserId String? createdByUser User? @relation(name: "executedByUser", fields: [createdByUserId], references: [id]) appliesToAllTenants Boolean @default(false) blockRuns WorkflowBlockExecution[] }

    model WorkflowInputExample { id String @id @default(cuid()) createdAt DateTime @default(now()) workflowId String workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) title String input String?

    @@unique([workflowId, title]) }

    model WorkflowBlockExecution { id String @id @default(cuid()) workflowExecutionId String workflowExecution WorkflowExecution @relation(fields: [workflowExecutionId], references: [id], onDelete: Cascade) workflowBlockId String workflowBlock WorkflowBlock @relation(name: "executions", fields: [workflowBlockId], references: [id], onDelete: Cascade) fromWorkflowBlockId String? fromWorkflowBlock WorkflowBlock? @relation(name: "fromExecutions", fields: [fromWorkflowBlockId], references: [id]) status String startedAt DateTime input String? output String? duration Int? endedAt DateTime? error String? }

    model WorkflowVariable { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) name String value String createdByUserId String? createdByUser User? @relation(fields: [userId], references: [id]) userId String?

    @@unique([tenantId, name]) }

    model WorkflowCredential { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) name String value String createdByUserId String? createdByUser User? @relation(fields: [userId], references: [id]) userId String?

    @@unique([tenantId, name]) }

    model IpAddress { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ip String @unique provider String type String countryCode String countryName String regionCode String regionName String city String zipCode String latitude Decimal? longitude Decimal? metadata String logs IpAddressLog[] }

    model IpAddressLog { id String @id @default(cuid()) createdAt DateTime @default(now()) ip String url String action String description String success Boolean error String? metadata String? ipAddressId String? ipAddress IpAddress? @relation(fields: [ipAddressId], references: [id]) }

    model Credit { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) userId String? user User? @relation(fields: [userId], references: [id]) amount Int type String objectId String?

    @@index([tenantId, userId]) @@index([tenantId, createdAt]) }

    model Feedback { id String @id @default(cuid()) createdAt DateTime @default(now()) tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) userId String? user User? @relation(fields: [userId], references: [id], onDelete: Cascade) message String fromUrl String }

    model Portal { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) createdByUserId String? createdByUser User? @relation(fields: [createdByUserId], references: [id], onDelete: Cascade) subdomain String @unique domain String? @unique title String isPublished Boolean @default(false) stripeAccountId String? themeColor String? themeScheme String? seoTitle String? seoDescription String? seoImage String? seoThumbnail String? seoTwitterCreator String? seoTwitterSite String? seoKeywords String? authRequireEmailVerification Boolean @default(false) authRequireOrganization Boolean @default(true) authRequireName Boolean @default(true) analyticsSimpleAnalytics Boolean @default(false) analyticsPlausibleAnalytics Boolean @default(false) analyticsGoogleAnalyticsTrackingId String? brandingLogo String? brandingLogoDarkMode String? brandingIcon String? brandingIconDarkMode String? brandingFavicon String? affiliatesRewardfulApiKey String? affiliatesRewardfulUrl String? metadata Json? users PortalUser[] pages PortalPage[] registrations PortalUserRegistration[] subscriptionProducts PortalSubscriptionProduct[] subscriptionPrices PortalSubscriptionPrice[] subscriptionFeatures PortalSubscriptionFeature[] userSubscriptions PortalUserSubscription[] userSubscriptionProducts PortalUserSubscriptionProduct[] userSubscriptionProductPrices PortalUserSubscriptionProductPrice[] checkoutSessionStatus PortalCheckoutSessionStatus[] analyticsUniqueVisitors AnalyticsUniqueVisitor[] analyticsPageViews AnalyticsPageView[] analyticsEvents AnalyticsEvent[] }

    model PortalUser { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenantId String? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) email String passwordHash String firstName String lastName String avatar String? phone String? verifyToken String? githubId String? googleId String? locale String? fromRegistration PortalUserRegistration? subscription PortalUserSubscription?

    @@unique([portalId, email]) @@unique([portalId, githubId]) @@unique([portalId, googleId]) @@index([portalId]) }

    model PortalPage { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) name String title String description String content String

    @@unique([portalId, name]) }

    model PortalUserRegistration { id String @id @default(cuid()) createdAt DateTime @default(now()) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) email String firstName String lastName String slug String? token String @unique ipAddress String? company String? selectedSubscriptionPriceId String? createdPortalUserId String? @unique createdPortalUser PortalUser? @relation(fields: [createdPortalUserId], references: [id], onDelete: Cascade)

    @@unique([portalId, email]) }

    model PortalSubscriptionProduct { id String @id @default(cuid()) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) stripeId String order Int title String active Boolean model Int public Boolean groupTitle String? groupDescription String? description String? badge String? billingAddressCollection String @default("auto") hasQuantity Boolean @default(false) canBuyAgain Boolean @default(false) prices PortalSubscriptionPrice[] features PortalSubscriptionFeature[] portalUserProducts PortalUserSubscriptionProduct[] }

    model PortalSubscriptionPrice { id String @id @default(cuid()) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) subscriptionProductId String subscriptionProduct PortalSubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade) stripeId String type Int billingPeriod Int price Decimal currency String trialDays Int active Boolean portalUserProductPrices PortalUserSubscriptionProductPrice[] }

    model PortalSubscriptionFeature { id String @id @default(cuid()) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) subscriptionProductId String order Int title String name String type Int value Int href String? badge String? accumulate Boolean @default(false) subscriptionProduct PortalSubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade) }

    model PortalUserSubscription { id String @id @default(cuid()) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) portalUserId String @unique stripeCustomerId String? portalUser PortalUser @relation(fields: [portalUserId], references: [id], onDelete: Cascade) products PortalUserSubscriptionProduct[] }

    model PortalUserSubscriptionProduct { id String @id @default(cuid()) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) portalUserSubscriptionId String subscriptionProductId String cancelledAt DateTime? endsAt DateTime? stripeSubscriptionId String? quantity Int? fromCheckoutSessionId String? currentPeriodStart DateTime? currentPeriodEnd DateTime? portalUserSubscription PortalUserSubscription @relation(fields: [portalUserSubscriptionId], references: [id], onDelete: Cascade) subscriptionProduct PortalSubscriptionProduct @relation(fields: [subscriptionProductId], references: [id]) prices PortalUserSubscriptionProductPrice[] }

    model PortalUserSubscriptionProductPrice { id String @id @default(cuid()) portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) portalUserSubscriptionProductId String portalUserSubscriptionProduct PortalUserSubscriptionProduct @relation(fields: [portalUserSubscriptionProductId], references: [id], onDelete: Cascade) subscriptionPriceId String? subscriptionPrice PortalSubscriptionPrice? @relation(fields: [subscriptionPriceId], references: [id]) }

    model PortalCheckoutSessionStatus { id String @unique portalId String portal Portal @relation(fields: [portalId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt pending Boolean @default(true) // has not added products to tenant email String fromUrl String fromUserId String? createdUserId String? }

    We respect your privacy.

    TLDR: We use cookies for language selection, theme, and analytics. Learn more.