specs
Data Model

Data Model — Convex Schema Reference

Status: Canonical Last Updated: 2026-05-11 Source: packages/backend/convex/schema.ts


Related specs


Entity Relationship Diagram

experiences (show templates)

    └── experienceEvents (scheduled instances)

              └── reservations (bookings)

                        ├── checkIns (QR scans)
                        └── bookingDrafts (in-progress bookings)

addOns (global add-on library)

    └── linked to reservations.addOns[]

users (staff/admin accounts)

    ├── tables (venue layout)
    │        │
    │        └── orders → orderItems → menuItems

    ├── guestProfiles (created on QR scan)
    │        │
    │        ├── guestReactions
    │        ├── photoSubmissions → photoLikes
    │        └── spinResults

    ├── challengeConfig
    │        │
    │        └── challengeSubmissions

    └── notifications

formSessions (inquiry form submissions)

    └── inquirySessions (admin overlay)

              └── inquiryFollowUps

payments (OnePay transaction tracking)

    └── linked to reservations

Tables

experiences (Level 1 — Show Templates)

FieldTypeDescription
codestringUnique code e.g. "CINE-GASTRO"
titlestringShow title
taglinestringShort description
descriptionstringFull rich text
embeddedVideostring?YouTube/Vimeo URL
gallerystring[]Image URLs
thumbnailUrlstring?Admin-configurable
supportedTicketTypes("DINNER_THEATRE" | "SHOW_ONLY")[]Ticket options
defaultDinnerPricenumberVND
defaultShowOnlyPricenumberVND
defaultCapacitynumberMax 32
status"ACTIVE" | "DRAFT" | "ARCHIVED"Lifecycle
slugstringURL-friendly ID
createdAt, updatedAtnumberUnix timestamps

Indexes: by_status, by_slug, by_code

experienceEvents (Level 2 — Scheduled Instances)

FieldTypeDescription
experienceIdId<"experiences">FK to template
codestringUnique e.g. "TMTL260521N"
datestringISO date "2026-05-21"
timestring"19:30"
dinnerPricenumberDenormalized
experienceOnlyPricenumberDenormalized
experienceOnlyEnabledbooleanAdmin toggle
actualCapacitynumberOverride possible
bookedCountnumberCalculated from PAID
thumbnailUrlstring?Per-event override
status"SCHEDULED" | "CANCELLED" | "SOLD_OUT"Event status
assignedTablesId<"tables">[]Linked tables
createdAt, updatedAtnumberUnix timestamps

Indexes: by_experience, by_date, by_experience_date, by_date_status, by_code

reservations (Level 3 — Bookings)

FieldTypeDescription
eventIdId<"experienceEvents">FK to event
customerFirstNamestring
customerLastNamestring
customerEmailstring
customerPhonestring?
customerNotestring?
ticketType"DINNER_THEATRE" | "SHOW_ONLY"
quantitynumberGuest count
bundleIdstring?Bundle pricing
guestsnumberSame as quantity
tableIdId<"tables">?Assigned table
addOns{addOnId, quantity}[]Selected add-ons
subtotalnumberBefore discounts
totalAmountnumberFinal price
paymentStatussee below
statussee below
paymentMethodstring?
onePayOrderIdstring?OnePay reference
qrCodestring?Generated QR
qrCodeUrlstring?QR image URL
tokenstring?Unique for QR scan
bookingExpiresAtnumber?Seat hold expiry
checkedInAtnumber?When scanned
discountAmountnumberBundle discount
discountPercentnumber
vipSurchargenumber
dayOfWeekSurchargenumberFriday/Saturday
smallPartySurchargenumber1-2 guests
bookingStepsee belowFlow tracking
createdAt, updatedAtnumber

paymentStatus values: PENDING, PAID, REFUNDED, FAILED, CANCELLED, REFUND_PENDING

status values: PENDING, PAID_CONFIRMED, CHECKED_IN, CANCELLED, REFUNDED

bookingStep values: EXPERIENCE, SHOW, TICKETS, ZONE, BUNDLE, ADDONS, CUSTOMER_INFO, PAYMENT, CONFIRMATION

Indexes: by_event, by_email, by_payment_status, by_expires, by_table, by_booking_step, by_token, by_vpcMerchTxnRef

addOns

FieldTypeDescription
namestring
descriptionstring
pricenumberVND
imageUrlstring?
type"COCKTAIL" | "FOOD" | "UPGRADE" | "OTHER"
enabledbooleanGlobal toggle
createdAt, updatedAtnumber

Indexes: by_enabled

users (Staff/Admin)

FieldTypeDescription
emailstringUnique
passwordHashstring?
role"ADMIN" | "STAFF"
namestring
createdAt, updatedAtnumber

Indexes: by_email, by_role

tables

FieldTypeDescription
namestring"T01", etc.
capacitynumber
status"AVAILABLE" | "OCCUPIED" | "RESERVED" | "MAINTENANCE"
position{x, y}?Floor plan
createdAt, updatedAtnumber

Indexes: by_status

menuItems

FieldTypeDescription
namestring
descriptionstring
pricenumberVND
categorysee below
station"KITCHEN" | "BAR"
imageUrlstring?
isAvailableboolean
isFeaturedboolean?
createdAt, updatedAtnumber

category values: FOOD, BEVERAGE, DESSERT, COCKTAIL, WINE, BEER

Indexes: by_category, by_available, by_station

orders

FieldTypeDescription
tableIdId<"tables">
reservationIdId<"reservations">?
eventIdId<"experienceEvents">?
status"OPEN" | "SUBMITTED" | "PREPARING" | "SERVED" | "PAID" | "CANCELLED"
subtotalnumber
totalAmountnumber
notesstring?
createdAt, updatedAtnumber

Indexes: by_table, by_status, by_event

orderItems

FieldTypeDescription
orderIdId<"orders">
menuItemIdId<"menuItems">
quantitynumber
unitPricenumber
totalPricenumber
notesstring?
status"PENDING" | "PREPARING" | "READY" | "SERVED"
station"KITCHEN" | "BAR"
isCompbooleanComplimentary
compSource"SPIN" | "PHOTO_WIN" | "GOOGLE_REVIEW"?
createdAtnumber

Indexes: by_order, by_order_status, by_station_status

guestProfiles

FieldTypeDescription
reservationIdId<"reservations">?
tableIdId<"tables">?
tokenstringQR identifier
avatarUrlstring?
avatarStorageIdstring?
nicknamestring
countrystring
originstring
moodTagsstring[]
biostring?
experienceDatestringISO date
checkedInboolean
createdAt, updatedAtnumber

Indexes: by_reservation, by_experience_date, by_token

guestReactions

FieldTypeDescription
fromProfileIdId<"guestProfiles">
toProfileIdId<"guestProfiles">
reactionType"WAVE" | "CHEERS" | "HEART"
experienceDatestring
createdAtnumber

Indexes: by_to_profile, by_from_profile, by_experience_date

bookingDrafts

FieldTypeDescription
sessionIdstringUser auth subject
eventIdId<"experienceEvents">?
experiencestring?
ticketType"DINNER_THEATRE" | "SHOW_ONLY"?
quantitynumber?
reservationIdId<"reservations">?
bookingExpiresAtnumber?
addOns{addOnId, quantity}[]?
bundlestring?
guestsnumber?
customerInfo{firstName, lastName, email, phone}?
currentStepsee below?
expiresAtnumberAuto-cleanup
createdAt, updatedAtnumber

currentStep values: EXPERIENCE, SHOW, TICKETS, BUNDLE, ADDONS, CUSTOMER_INFO, PAYMENT, CONFIRMATION

Indexes: by_session, by_expires

formSessions

FieldTypeDescription
sessionIdstringlocalStorage UUID
formTypesee below
datastringJSON
submittedboolean
expiresAtnumber7 days
createdAt, updatedAtnumber

formType values: CONTACT, VENUE_RENTAL, PRIVATE_EVENTS, WORKSHOPS, ARTIST_PROPOSAL, HOST_AN_EVENT, FRENCH_MENTALIST, DINNER_THEATER, CHECKOUT, RESERVATION, PROFILE, EXPERIENCE, ADDON

Indexes: by_session_type, by_expires, by_data_search

inquirySessions

FieldTypeDescription
formSessionIdId<"formSessions">
formTypesame as formSessions
status"NEW" | "READ" | "REPLIED" | "ARCHIVED"
adminNotesstring?
reviewedBystring?Clerk user ID
reviewedAtnumber?
createdAt, updatedAtnumber

Indexes: by_formSession, by_formType, by_status, by_formType_status

inquiryFollowUps

FieldTypeDescription
inquiryIdId<"inquirySessions">
authorIdstringClerk user ID
authorNamestring
contentstring
createdAtnumber

Indexes: by_inquiry

challengeConfig

FieldTypeDescription
challengeType"PHOTO_WALL" | "LUCKY_SPIN" | "GOOGLE_REVIEW"
enabledboolean
maxValuenumber?
prizeDescriptionstring?
steps{order, text, imageUrl?}[]
activeForDatesstring[]
createdAt, updatedAtnumber

Indexes: by_type

photoSubmissions

FieldTypeDescription
profileIdId<"guestProfiles">
orderIdId<"orders">?
tableIdId<"tables">
imageUrlstring
captionstring?
likeCountnumber
status"ACTIVE" | "HIDDEN"
winnerboolean
experienceDatestring
createdAt, updatedAtnumber

Indexes: by_experience_date, by_profile, by_status, by_table_experience, by_likes

photoLikes

FieldTypeDescription
submissionIdId<"photoSubmissions">
profileIdId<"guestProfiles">
createdAtnumber

Indexes: by_submission, by_profile_submission, by_profile

spinPrizes

FieldTypeDescription
labelstring
prizeType"MENU_ITEM" | "DISCOUNT" | "FREE_ITEM"
menuItemIdId<"menuItems">?
discountPercentnumber?
weightnumber
enabledboolean
createdAt, updatedAtnumber

Indexes: by_enabled

spinResults

FieldTypeDescription
profileIdId<"guestProfiles">
orderIdId<"orders">
tableIdId<"tables">
prizeIdId<"spinPrizes">
displayTextstring
experienceDatestring
createdAt, updatedAtnumber

Indexes: by_experience_date, by_table_experience, by_profile

challengeSubmissions

FieldTypeDescription
profileIdId<"guestProfiles">
orderIdId<"orders">
tableIdId<"tables">
challengeType"GOOGLE_REVIEW"
screenshotUrlstring
status"PENDING" | "APPROVED" | "REJECTED"
rewardMenuItemIdId<"menuItems">?
reviewedById<"users">?
reviewedAtnumber?
notesstring?
experienceDatestring
createdAt, updatedAtnumber

Indexes: by_status, by_experience_date, by_table_experience

notifications

FieldTypeDescription
type"ORDER_READY" | "NEW_RESERVATION" | "EXPERIENCE_REMINDER" | "ALERT" | "SYSTEM"
titlestring
messagestring
isReadboolean
priority"LOW" | "MEDIUM" | "HIGH"
metadatastring?JSON
createdAtnumber

Indexes: by_read, by_type, by_created

notificationLogs

FieldTypeDescription
notificationTypesee below
channel"EMAIL" | "WHATSAPP"
recipientstring
subjectstring?
status"SUCCESS" | "FAILED"
errorMessagestring?
reservationIdId<"reservations">?
createdAtnumber

notificationType values: EMAIL_CONFIRMATION, EMAIL_CANCELLATION, WHATSAPP_CONFIRMATION, EMAIL_ADMIN_NEW_BOOKING

Indexes: by_reservation, by_status, by_created

zohoSyncLogs

FieldTypeDescription
operationTypesee below
entityTypesee below
entityIdstring?Zoho ID
status"SUCCESS" | "FAILED"
errorMessagestring?
reservationIdId<"reservations">?
createdAtnumber

operationType values: CONTACT_UPSERT, DEAL_CREATE, INVOICE_CREATE, INVOICE_MARK_PAID

entityType values: CONTACT, DEAL, INVOICE

Indexes: by_reservation, by_status, by_created

payments

FieldTypeDescription
reservationIdId<"reservations">
vpcMerchTxnRefstringOnePay reference
vpcTransactionNostring?
amountnumber
currencystring
status"PENDING" | "SUCCESS" | "FAILED"
responseCodestring?
messagestring?
cardstring?
cardNumstring?
createdAt, updatedAtnumber

Indexes: by_vpcMerchTxnRef

checkIns

FieldTypeDescription
ticketIdstringReservation token
eventIdId<"experienceEvents">
checkedInAtnumberUnix timestamp
checkedInBystring?Staff user ID

Indexes: by_ticket, by_event