Schema Overview
Evaly uses Convex’s built-in database with TypeScript schema validation. All schemas are defined inconvex/schemas/ and exported from convex/schema.ts.
Core Entities
Multi-Tenancy
organization
organization
Organizations are the top-level multi-tenancy container.Fields:
name- Organization nameimage- Optional organization logo URLtype- Organization type identifierdeletedAt- Soft delete timestamp
- Has many
organizer(members) - Has many
test - Has one
organizationPlan - Has one
organizationUsage
organizer
organizer
Organizers are members of an organization with test management permissions.Fields:
userId- Reference touserstableorganizationId- Reference toorganizationrole- Member role (owner, admin, member)deletedAt- Soft delete timestamp
by_user_idby_organization_id
organizationInvitation
organizationInvitation
Token-based invitation system for adding members.Fields:
organizationId- Reference toorganizationemail- Invitee email addresstoken- Unique invitation tokenrole- Invited roleinvitedBy- Reference to inviter’sorganizerIDexpiresAt- Token expiration (7 days from creation)acceptedAt- Optional acceptance timestampdeletedAt- Soft delete timestamp
by_tokenby_organization_idby_email
Subscription & Usage
organizationPlan
organizationPlan
Subscription plan configuration for each organization.Fields:
organizationId- Reference toorganizationplan- Plan type:"free" | "pro" | "max"customLimits- Optional custom limit overridesaiQuestionGeneration- Monthly AI question generation limitaiTranslation- Monthly AI translation limitaiOptions- Monthly AI options generation limitaiAnalysis- Monthly AI analysis limitresults- Monthly test results limitmembers- Team members limitactiveTests- Concurrent active tests limitconcurrentParticipants- Concurrent participants limit
createdAt- Plan creation timestampupdatedAt- Plan update timestamp
by_organization
organizationUsage
organizationUsage
Tracks monthly usage against plan limits.Fields:
organizationId- Reference toorganizationmonth- Usage month (YYYY-MM format)aiQuestionGeneration- AI questions generated this monthaiTranslation- AI translations this monthaiOptions- AI options generated this monthaiAnalysis- AI analyses this monthresults- Test results generated this monthupdatedAt- Last update timestamp
by_organization_month
Test Structure
test
test
Core test/exam configuration.Fields:
title- Test titledescription- Optional test descriptionorganizationId- Reference toorganizationcreatedByOrganizerId- Reference to creator’sorganizerIDaccess- Access control:"public" | "private"isPublished- Publication statusheldAt- Optional test date/locationdeletedAt- Soft delete timestamp
scheduledStartAt- Optional start timestampscheduledEndAt- Optional end timestampactivationJobId- Scheduler job ID for activationfinishJobId- Scheduler job ID for finish
pausedAt- Pause timestamp (if paused)totalPausedDuration- Total time paused (milliseconds)useSectionDurations- Whether to use section-based durationsfinishedAt- Test completion timestampstoppedReason- Optional stop/cancel reason
password- Optional password protectionallowedEmailDomains- Optional email domain whitelistallowedIpAddresses- Optional IP address whitelist
randomizeQuestions- Randomize question orderrandomizeAnswers- Randomize answer optionsresultsReleased- Whether participants can view results
type- Old type field (useaccessinstead)showResultImmediately- Old results field (useresultsReleased)
by_organization_id
testSection
testSection
Sections organize questions within a test.Fields:
testId- Reference totesttitle- Section titledescription- Optional section descriptionorder- Display orderduration- Optional duration in minutes (for timed sections)deletedAt- Soft delete timestamp
by_test_id
question
question
Individual questions within sections or libraries.Fields:
question- Question text (rich text HTML)referenceId- Section or library ID (string, not typed ID)originalReferenceId- Original library ID (for duplicated questions)organizationId- Reference toorganizationorder- Display ordertype- Question type:"multiple-choice""yes-or-no""image-choice""audio-choice""text-field""file-upload""fill-the-blank""audio-response""video-response""matching-pairs""slider-scale""likert-scale""ranking"
pointValue- Optional point value for scoringallowMultipleAnswers- Whether multiple answers are allowedoptions- Answer options array (for choice questions)id- Option IDtext- Option textisCorrect- Correct answer flagmediaUrl- Optional media URLmediaType- Optional media typepointValue- Optional partial credit
settings- Type-specific settings (see below)deletedAt- Soft delete timestamp
- Text Field:
placeholderText,minCharacterLimit,maxCharacterLimit - Audio/Video:
maxDurationSeconds - Likert Scale:
likertScalePoints,likertLabels - Slider Scale:
sliderMin,sliderMax,sliderStep,sliderMinLabel,sliderMaxLabel,sliderShowValue - Ranking:
rankingScoringType("exact" | "per-position")
by_reference_id
questionLibrary
questionLibrary
Reusable question banks for organizing questions.Fields:
name- Library namedescription- Optional library descriptionorganizationId- Reference toorganizationdeletedAt- Soft delete timestamp
by_organization_id
Questions reference libraries via
referenceId field (string format, not typed ID).Participant Data
testParticipant
testParticipant
Individual participants allowed to take a test.Fields:
testId- Reference totestemail- Participant emailaddedAt- Timestamp when addeddeletedAt- Soft delete timestamp
by_test_idby_emailby_test_id_email
testParticipantGroup
testParticipantGroup
User groups allowed to take a test.Fields:
testId- Reference totestuserGroupId- Reference touserGroupaddedAt- Timestamp when addeddeletedAt- Soft delete timestamp
by_test_idby_user_group_id
userGroup
userGroup
Groups for organizing participants.Fields:
name- Group nameorganizationId- Reference toorganizationdeletedAt- Soft delete timestamp
by_organization_id
userGroupMember
userGroupMember
Members of user groups.Fields:
userGroupId- Reference touserGroupemail- Member emailaddedAt- Timestamp when addeddeletedAt- Soft delete timestamp
by_user_group_idby_email
testAttempt
testAttempt
Participant attempts at test sections.Fields:
testId- Reference totesttestSectionId- Reference totestSectionparticipantId- Reference tousersstartedAt- Attempt start timestampfinishedAt- Optional completion timestampdeletedAt- Soft delete timestamp
by_participant_test_sectionby_participant_testby_test
Each section of a test creates a separate attempt record.
testAttemptAnswer
testAttemptAnswer
Individual answers to questions within attempts.Fields:
testAttemptId- Reference totestAttempttestId- Reference totesttestSectionId- Reference totestSectionquestionId- Reference toquestionanswerText- Optional text answeranswerOptions- Optional selected option IDsanswerMediaUrl- Optional uploaded media URLanswerMediaMetadata- Optional media metadataextension- File extensionsize- File size in bytesname- Original filenamedurationMs- Optional duration for audio/video
isFlagged- Whether answer is flagged for reviewdeletedAt- Soft delete timestamp
by_attempt_and_questionby_attemptby_sectionby_test
testSession
testSession
Session tracking for participants.Fields:
testId- Reference totestparticipantId- Reference touserssessionToken- Unique session tokenstartedAt- Session start timestamplastActivityAt- Last activity timestampdeletedAt- Soft delete timestamp
by_test_idby_participant_idby_session_token
Grading & Results
questionGrade
questionGrade
Manual grading records for subjective questions.Fields:
testAttemptId- Reference totestAttemptquestionId- Reference toquestionparticipantId- Reference touserstestId- Reference totestpointsAwarded- Points awarded for this answerfeedback- Optional grader feedbackgradedBy- Reference to grader’sorganizerIDgradedAt- Grading timestampdeletedAt- Soft delete timestamp
by_test_attemptby_questionby_participantby_test
Used for manual grading of text fields, file uploads, and other subjective question types.
Activity & Notifications
testActivityLog
testActivityLog
Audit log for test events.Fields:
testId- Reference totestorganizationId- Reference toorganizationeventType- Event type (e.g.,"test_started","test_paused","participant_joined")participantId- Optional participant IDorganizerId- Optional organizer IDmetadata- Optional event-specific datatimestamp- Event timestamp
by_test_idby_organization_idby_event_type
notification
notification
Individual notification events (batched for display).Fields:
organizationId- Reference toorganizationtype- Notification typebatchKey- Batching key for groupingtestId- Optional test IDtestName- Optional test nameparticipantId- Optional participant IDparticipantName- Optional participant nameparticipantEmail- Optional participant emailmetadata- Optional additional datasectionNamereasonlimitcurrentmemberNamememberEmail
createdAt- Notification timestamp
by_organizationby_batch_keyby_organization_created
notificationBatch
notificationBatch
Grouped notifications for display.Fields:
organizationId- Reference toorganizationbatchKey- Unique batch keytype- Notification typecount- Number of events in batchlatestEventAt- Latest event timestampfirstEventAt- First event timestamp
by_organizationby_batch_key
notificationRead
notificationRead
Per-organizer read status tracking.Fields:
organizerId- Reference toorganizerbatchKey- Reference to notification batchreadAt- Read timestamp
by_organizerby_batch_keyby_organizer_batch
organizerNotificationPreferences
organizerNotificationPreferences
Per-organizer notification preferences.Fields:
organizerId- Reference toorganizer- Individual preference flags for each notification type
by_organizer
AI Features
aiThreads
aiThreads
AI conversation threads for question generation.Fields:
organizationId- Reference toorganizationorganizerId- Reference toorganizerreferenceId- Section or library IDcreatedAt- Thread creation timestampdeletedAt- Soft delete timestamp
by_organization_idby_reference_id
aiThreadMessages
aiThreadMessages
Messages within AI threads.Fields:
threadId- Reference toaiThreadsrole- Message role:"user" | "assistant" | "system"content- Message contentcreatedAt- Message timestamp
by_thread_id
Authentication
users
users
User accounts (managed by Convex Auth).Fields:
- Standard Convex Auth fields
selectedOrganizationId- Current organization contextselectedOrganizerId- Current organizer context
Extended from Convex Auth’s base user schema.
passwordResetTokens
passwordResetTokens
Password reset token management.Fields:
userId- Reference touserstoken- Unique reset tokenexpiresAt- Token expiration timestampusedAt- Optional usage timestamp
by_tokenby_user_id
emailVerificationTokens
emailVerificationTokens
Email verification token management.Fields:
userId- Reference touserstoken- Unique verification tokenexpiresAt- Token expiration timestampverifiedAt- Optional verification timestamp
by_tokenby_user_id
Schema Patterns
Soft Deletion
Most tables include adeletedAt field for soft deletion:
Ordering
Many collections have anorder field for manual sorting:
testSection.orderquestion.order- User-controlled ordering via drag-and-drop
Reference IDs
Questions use flexible string-based references:referenceId- Points to section ID or library IDoriginalReferenceId- Tracks source when duplicated from library
Timestamps
_creationTime- Automatic Convex timestamp- Custom timestamps:
startedAt,finishedAt,createdAt,updatedAt
Indexes
Indexes optimize query performance:- Single field:
by_organization_id,by_test_id - Compound:
by_organization_created,by_participant_test_section - Unique lookups:
by_token,by_session_token