Database Schema
MysticX uses PostgreSQL 18 with Prisma 7 as the ORM. The schema has 40+ models organized into logical domains.
Core Models
User
Central user record.
| Field | Type | Description |
|---|---|---|
| id | String | Primary key |
| name | String | Display name |
| String | Email (unique) | |
| emailVerified | Boolean | Email verification status |
| image | String? | Avatar URL |
| credits | Int | Current credit balance |
| tier | SubscriptionTier | FREE, GOLD, or DIAMOND |
| role | String? | 'admin' for admin users, null for regular users |
| timezone | String? | IANA timezone |
| stripeCustomerId | String? | Stripe customer ID |
| subscriptionProvider | String? | 'stripe', 'apple', 'google', or null |
| subscriptionInterval | String? | 'week', 'month', 'year', or null |
| inviteCode | String? | Unique referral code |
| invitedByUserId | String? | ID of user who invited this user |
| activeCardSkinId | String? | Currently equipped card skin |
| activeTarotReaderId | String? | Currently selected reader |
| banned | Boolean? | Account ban status |
| banReason | String? | Reason for ban |
| banExpires | DateTime? | Ban expiry (null = permanent) |
| deletedAt | DateTime? | Soft-delete timestamp (null = active)ntly selected reader |
| banned | Boolean? | Account ban status |
| banReason | String? | Reason for ban |
| banExpires | DateTime? | Ban expiry (null = permanent) |
| deletedAt | DateTime? | Soft-delete timestamp (null = active) |
Session / Account / Verification
Standard Better Auth tables for session management, OAuth accounts, and email verification tokens.
UserProfile
Optional personal profile used to enrich AI readings.
| Field | Type | Description |
|---|---|---|
| pronouns | String? | User's pronouns (max 20 chars) |
| birthday | Date? | Birthday (date only, no time) |
| zodiacSign | String? | Zodiac sign; auto-computed from birthday if null |
| relationship | String? | Relationship status |
| bio | String? | Short bio (max 200 chars) |
| occupation | String? | Occupation (max 100 chars) |
Reading Models
TarotReading
Core reading record.
| Field | Type | Description |
|---|---|---|
| id | String | Primary key |
| userId | String? | Owner (null for guest readings) |
| guestToken | String? | Guest identifier |
| readerId | String | AI reader persona used |
| spreadId | String | Spread type used |
| question | String | User's question |
| selectedCards | Json | Array of card indices and reversed status |
| locale | String | Reading locale |
| status | ReadingStatus | ACTIVE (live reading) or PRECREATED (pre-created during card selection, not yet claimed) |
| ipAddress | String? | Client IP |
| timezone | String? | Client timezone |
| location | String? | Approximate location derived from IP |
Chat
One chat per reading for follow-up conversations.
Message
Individual messages in a chat.
| Field | Type | Description |
|---|---|---|
| role | String | user, assistant, system |
| type | String | Message type |
| content | String | Message body |
| status | MessageStatus | PENDING, STREAMING, COMPLETED, FAILED |
| tokensCount | Int? | Token count for billing |
| feedbackRating | FeedbackRating? | THUMBS_UP or THUMBS_DOWN (null = no reaction) |
| feedbackReason | String? | Text reason when THUMBS_DOWN |
AiApiCall
Telemetry for every AI model call.
| Field | Type | Description |
|---|---|---|
| callType | AiCallType | INITIAL_READING, FOLLOW_UP, DRAW_CARD, SPREAD_SUGGEST |
| model | String | Model identifier |
| status | AiCallStatus | SUCCESS, FAILED, TIMEOUT |
| totalTokens | Int? | Combined token count |
| durationMs | Int | Total call duration |
| timeToFirstTokenMs | Int? | Time to first streamed token |
| inputTokens | Int? | Input token count |
| outputTokens | Int? | Output token count |
| totalTokens | Int? | Combined token count |
| errorMessage | String? | Error details if failed |
Tarot Content Models
TarotCard
78-card deck with localized names and meanings.
| Field | Type | Description |
|---|---|---|
| cardIndex | Int | 0–77 index |
| suit | String | 'major', 'wands', 'cups', 'swords', 'pentacles' |
| rank | String | '0'–'21' for major, 'ace'–'king' for minor |
| name | Json | TLocalizedString |
| imageUrl | String | Card face image |
| description | Json | TLocalizedString |
| meaningUp | Json | Upright meaning (localized) |
| meaningRev | Json | Reversed meaning (localized) |
| keywordsUp | Json | Upright keywords |
| keywordsRev | Json | Reversed keywords |
| yesNoUpVerdict | YesNoVerdict | YES, NO, or MAYBE for upright |
| yesNoUpStrength | Int? | Strength 1–10 |
| yesNoUpMeaning | Json? | TLocalizedString for upright meaning |
| yesNoRevVerdict | YesNoVerdict | YES, NO, or MAYBE for reversed |
| yesNoRevStrength | Int? | Strength 1–10 |
| yesNoRevMeaning | Json? | TLocalizedString for reversed meaning |
TarotSpread
Spread definitions with positions.
| Field | Type | DescTotal number of cards drawn (includes any system-drawn cards) | | userSelectionCount | Int | How many cards the user manually picks | | positions | Json | Position definitions (order, name, description, isMainCard) | | layoutImageUrl | String? | Small image showing the spread layout | | sortOrder | Int | Admin-reorderable display ordering | slug | String | URL slug (e.g., celtic-cross) | | name | Json | TLocalizedString | | description | Json | TLocalizedString | | cardsCount | Int | Total number of cards drawn (includes any system-drawn cards) | | userSelectionCount | Int | How many cards the user manually picks | | positions | Json | Position definitions (order, name, description, isMainCard) | | layoutImageUrl | String? | Small image showing the spread layout | | sortOrder | Int | Admin-reorderable display ordering | isDefault | Boolean | Whether this is the default reader | | isActive | Boolean | Soft-delete flag (hidden when false) | | name | Json | TLocalizedString | | avatarUrl | String? | Avatar image URL | | coverImageUrl | String? | Background cover image URL | | bio | Json | Short bio shown in reader selection (TLocalizedString) | | description | Json | Detailed backstory (TLocalizedString) | | specialties | Json | TLocalized<string[]> — reader's specialty tags | | introAudioUrls | Json? | Partial<TLocalizedString> — per-locale intro audio URLs | | voiceIds | Json? | Partial<TLocalizedString> — MiniMax voice IDs per locale | | systemPrompt | String | AI system prompt (English only, not user-facing) | | temperature | Float | Model temperature (0.0–1.0) | | topP | Float | Top-P sampling parameter | | thinkingLevel | ThinkingLevel | AI thinking intensity | | unlockPrice | Int | Credit cost to unlock (0 = free) | | sortOrder | Int | UI display order
| Field | Type | Description |
|---|---|---|
| slug | String | URL slug |
| isDefault | Boolean | Whether this is the default reader |
| isActive | Boolean | Soft-delete flag (hidden when false) |
| name | Json | TLocalizedString |
| avatarUrl | String? | Avatar image URL |
| coverImageUrl | String? | Background cover image URL |
| bio | Json | Short bio shown in reader selection (TLocalizedString) |
| description | Json | Detailed backstory (TLocalizedString) |
| specialties | Json | TLocalized<string[]> — reader's specialty tags |
| introAudioUrls | Json? | Partial<TLocalizedString> — per-locale intro audio URLs |
| voiceIds | Json? | Partial<TLocalizedString> — MiniMax voice IDs per locale |
| systemPrompt | String | AI system prompt (English only, not user-facing) |
| temperature | Float | Model temperature (0.0–1.0) |
| topP | Float | Top-P sampling parameter |
| thinkingLevel | ThinkingLevel | AI thinking intensity |
| unlockPrice | Int | Credit cost to unlock (0 = free) |
| sortOrder | Int | UI display order |
TarotQuestionCategory / TarotQuestion / SpreadQuestion / SpreadInspiration
Question catalog and inspiration content for spread selection.
- TarotQuestionCategory — Categories (Love, Career, Healing, Decision) with icons, localized labels, and sort order.
- TarotQuestion — Individual questions per category with depth level (
STRAIGHTFORWARD,LIGHT,MEDIUM,DEEP) and sort order. - SpreadQuestion — Per-spread prompt questions shown in the hero section during spread selection.
- SpreadInspiration — Short mystical sentences shown on the loading screen after card reveal; one pool per spread.
Commerce Models
Subscription
See enum values below | | description | String?cord linked to user.
| Field | Type | Description |
|---|---|---|
| plan | String | GOLD or DIAMOND |
| stripeSubscriptionId | String | Stripe sub ID |
| status | String | active, canceled, past_due, etc. |
| periodStart | DateTime | Current period start |
| periodEnd | DateTime | Current period end |
CreditTransaction
Audit log for every credit change.
| Field | Type | Description |
|---|---|---|
| userId | String | User |
| amount | Int | Positive (credit) or negative (debit) |
| type | CreditTransactionType | See enum values below |
| description | String? | Human-readable description |
Personalization Models
UserMemory
AI memory facts per user.
| Field | Type | Description |
|---|---|---|
| facts | Json | Array of up to 5 English-language fact strings |
| translations | Json | On-demand locale translations of facts |
WeeklyGuidanceof markdown content |
| status | GuidanceStatus | GENERATING, READY, FAILED | | themes | Json | Extracted theme strings | | themeTranslations | Json | Cached locale translations of themes | | readingCount | Int | Number of r
| Field | Type | Description |
|---|---|---|
| content | String | Markdown guidance content |
| translations | Json | Localized versions |
| status | GuidanceStatus | GENERATING, READY, FAILED |
| weekStart / weekEnd | DateTime | Covered period |
| autoGenerated | Boolean | True for Diamond auto-generation |
SoulJourney
Personal evolution document.
| Field | Type | Description |
|---|---|---|
| content | String | Markdown journey content |
| translations | Json | Localized versions of markdown content |
| status | GuidanceStatus | GENERATING, READY, FAILED |
| themes | Json | Extracted theme strings |
| themeTranslations | Json | Cached locale translations of themes |
| readingCount | Int | Number of readings analyzed |
CardOfDayEntry
Daily card draws.
| Field | Type | Description |
|---|---|---|
| cardIndex | Int | Drawn card |
| isReversed | Boolean | Card orientation |
| keywords | String[] | Array of keyword strings |
| emotionalWeather | String | AI-generated mood paragraph |
| actions | String[] | Recommended actions (3 items) |
| questions | String[] | Exploration prompts (3 items) |
| resonanceCount | Int | User resonance tracking |
| credits | Int | Credits awarded |
Engagement Models
Referral
Tracks invitation relationships.
| Field | Type | Description |
|---|---|---|
| inviterUserId | String | Who invited |
| inviteeUserId | String | Who was invited |
| status | ReferralStatus | REGISTERED, QUALIFIED, REWARDED |
ReferralMilestoneReward
Milestone bonus tracking (3, 10, 25 referrals).
Notification
In-app notification records.
| Field | Type | Description |
|---|---|---|
| category | NotificationCategory | READING, ACCOUNT, SOCIAL, SYSTEM |
| type | NotificationType | Specific notification type enum |
| targetUrl | String? | Deep link URL |
| metadata | Json? | Optional payload |
| isRead | Boolean | Read status |
UserFeedback
User-submitted feedback with screenshot URLs.
Content Models
BlogPost / BlogCategory / BlogTag / BlogAuthor / BlogPostTag
Full blog system with categories, tags, and author profiles. All content fields use TLocalizedString JSON format.
Marketplace Models
CardSkin / UserCardSkin
Card back designs and user ownership records.
UserTarotReader
Reader unlock records (which readers a user has purchased). filteredCount | Int | How many passed pre-filter (non-gibberish) | | categories | Json | Category distribution map | | topKeywords | Json | Top keywords per category | | wordCloud | Json | Word cloud data array | | samplesByKeyword | Json | Sample questions per keyword | | trendData | Json | Monthly trend data | | summary | String? | AI-generated narrative | | actionItems | Json | Recommended actions list | | tokensUsed | Int | AI tokens consumed | | durationMs | Int | Total wall-clock time for analysis | | triggeredBy | String? | Admin user ID who triggered | | completedAt | DateTime? | When analysis complet
Links Telegram accounts to MysticX user accounts (1:1 per user).
InsightSnapshot
Admin-triggered AI analysis snapshots of user question patterns.
| Field | Type | Description |
|---|---|---|
| status | InsightSnapshotStatus | PENDING, RUNNING, DONE, FAILED |
| sampledCount | Int | How many were sampled |
| filteredCount | Int | How many passed pre-filter (non-gibberish) |
| categories | Json | Category distribution map |
| topKeywords | Json | Top keywords per category |
| wordCloud | Json | Word cloud data array |
| samplesByKeyword | Json | Sample questions per keyword |
| trendData | Json | Monthly trend data |
| summary | String? | AI-generated narrative |
| actionItems | Json | Recommended actions list |
| tokensUsed | Int | AI tokens consumed |
| durationMs | Int | Total wall-clock time for analysis |
| triggeredBy | String? | Admin user ID who triggered |
| completedAt | DateTime? | When analysis completed |
DeletedEmail
Tombstone table that prevents re-registration bonus abuse. Stores emails of deleted accounts permanently (no FK to User — must survive hard deletes).
Key Enums
| Enum | Values |
|---|---|
| SubscriptionTier | FREE, GOLD, DIAMOND |
| CreditTransactionType | REGISTRATION_BONUS, REFERRAL_INVITER_REWARD, REFERRAL_INVITEE_BONUS, REFERRAL_MILESTONE_BONUS, DAILY_CLAIM, CARD_OF_DAY_CLAIM, READING_DEDUCTION, FOLLOWUP_DEDUCTION, SPREAD_SUGGEST_DEDUCTION, SUBSCRIPTION_GRANT, ONE_TIME_PURCHASE, ADMIN_ADJUSTMENT, WEEKLY_GUIDANCE_DEDUCTION, SOUL_JOURNEY_DEDUCTION, READER_UNLOCK_DEDUCTION, CARD_SKIN_UNLOCK_DEDUCTION |
| ReadingStatus | ACTIVE, PRECREATED |
| MessageStatus | PENDING, STREAMING, COMPLETED, FAILED |
| FeedbackRating | THUMBS_UP, THUMBS_DOWN |
| GuidanceStatus | GENERATING, READY, FAILED |
| ReferralStatus | REGISTERED, QUALIFIED, REWARDED |
| FeedbackStatus | PENDING, REVIEWED, RESOLVED |
| FeedbackMood | AMAZING, GOOD, OKAY, BAD, TERRIBLE |
| BlogPostStatus | DRAFT, PUBLISHED, SCHEDULED, ARCHIVED |
| NotificationCategory | READING, ACCOUNT, SOCIAL, SYSTEM |
| NotificationType | WEEKLY_GUIDANCE_GENERATED, SOUL_JOURNEY_GENERATED, SUBSCRIPTION_CHANGED, REFERRAL_REWARD_EARNED, REFERRAL_MILESTONE_REACHED, SYSTEM_MAINTENANCE, SYSTEM_PRODUCT_CHANGE |
| AiCallType | INITIAL_READING, FOLLOW_UP, DRAW_CARD, SPREAD_SUGGEST |
| AiCallStatus | SUCCESS, FAILED, TIMEOUT |
| ThinkingLevel | MINIMAL, LOW, MEDIUM, HIGH |
| YesNoVerdict | YES, NO, MAYBE |
| QuestionLevel | STRAIGHTFORWARD, LIGHT, MEDIUM, DEEP |
| InsightSnapshotStatus | PENDING, RUNNING, DONE, FAILED |