authholds identity and SaaS billing: users, sessions, organizations, members, purchases and coach web notifications. better-auth owns the shape of most of these tables.publicholds all domain data: studios, coaches, trainees, programs, libraries, tracking, forms, content, messaging, automation, calendar and CRM.
Core entities
Dotted lines are logical links stored as plain ids with no foreign key. They matter because nothing in the database stops them from dangling.Conventions
Ids
Every model uses a string primary key with@default(cuid()). Two exceptions: FoodBarcodeProduct is keyed by its barcode, and ContentItemTag has a composite key of itemId and tagId.
Tenancy
studioId is the tenant column in the public schema. Repositories always filter by the studioId taken from req.auth, never from the request body. Models fall into four groups:
Client scoped tables have no
studioId. Any query on them must first prove the client belongs to the caller’s studio.
Per-studio overrides of global rows
Global exercises and foods are never edited in place. A studio’s edit lands in an override table keyed by(studioId, exerciseId) or (studioId, foodId):
ExerciseStudioOverride(physical tableexercise_video_overrides). Every field is nullable andnullmeans “inherit”.FoodLibraryOverride. A full copy of the editable food fields.
Soft delete
Three models carrydeletedAt: Studio, Client and Lead. Everything else is hard deleted, mostly through onDelete: Cascade from the studio or the client.
Money
Prices are integers in agorot (1/100 of a shekel):Product.priceAgorot, Client.priceAgorot, ClientSubscription.priceAgorot, Lead.valueAgorot. null means “not set”. 0 is a real price, a free plan.
Dates
Columns typed@db.Date are calendar days with no time: DailyMetric.date, NutritionDayLog.date, CardioLog.performedOn, ClientAppDay.date, Client.birthDate, Client.updateNextOn, Client.updateSkipOn, AvailabilityWindow.date. Everything else is a timestamp. Studio.timezone defaults to Asia/Jerusalem, and Client.timezone holds the trainee device’s zone when the app has reported it.
String columns that behave like enums
Several status columns are plainString validated in code, not Postgres enums. This avoids enum migrations. Examples: AutomationFlowRun.status, AutofitImport.status, AutofitExerciseMatch.status, AdminDeletion.status, ContentItem.type, HomeBanner.source, ClientActivity.actorType, ClientPhoto.pose. The allowed values are listed on each domain page.
JSON columns
Structured documents live inJson columns and are validated by Zod in the API. The large ones have their own pages:
Indexes worth knowing
- Trigram GIN indexes (
gin_trgm_ops, extensionpg_trgm) back free-text search onClient.name,Client.phone,Client.email,ExerciseLibraryItem.name,ExerciseLibraryItem.searchTextandFoodLibraryItem.name. - GIN indexes on array columns:
ExerciseLibraryItem.targetMuscles,secondaryMuscles,bodyParts,exerciseTypes, andInboxItem.coachIds. - Some constraints exist only in SQL because Prisma cannot express them. See The two Prisma schemas.
Pages by domain
Identity and auth
User, Session, Account, Verification, Passkey, TwoFactor, Organization, Member, Invitation.
Billing
Purchase and the customer ids on User and Organization.
Studios and coaches
Studio, StudioApiKey, Coach, ClientCoach.
Clients, products, subscriptions
Client, Product, ClientSubscription and their status enums.
Programs and templates
ProgramFolder, ProgramTemplate, Program, TraineeExerciseSubstitution.
Exercise and food libraries
Library items, studio overrides, source maps, barcode cache, shopping list.
Tracking
Workout, cardio, daily metrics, health samples, weight, photos, meals.
Forms
Templates, assignments, responses, signing, check-in reviews.
Content and banner
Content items, categories, tags, per-trainee access, home banner.
Messaging and notifications
WhatsApp, push tokens, notification configs, coach notifications.
Automation and tasks
Inbox items, task automations, flow runs, hooks, rules.
Calendar
Events and availability windows.
CRM, audit and misc
Leads, activity feeds, audit log, saved filters, AutoFit import, admin deletions.
The two Prisma schemas
Which file drives what, and how to change a model safely.