Exams
Backoffice paths: backoffice/exam-pools, backoffice/exam-templates, backoffice/exam-assignments, backoffice/exams/{id}/grade
User paths: exams, exams/{id}/take, exams/{id}/results
Available to roles: System Administrator, Administrator (full management); Operations Staff can view assignments and grade exams
Overview
The exams system allows airlines to require pilots to pass knowledge tests before receiving licenses or earning rank promotions. Staff build question pools, assemble exam templates that draw random questions from those pools, and assign exams to pilots either manually or automatically through the license purchase and rank promotion workflows.
The system supports three question types (multiple choice, true/false, and free text), timed exams with auto-submission, automatic grading for objective questions, and optional AI-assisted grading suggestions for free text answers.
Question Pools
Backoffice path: backoffice/exam-pools
Question pools are collections of related questions grouped by topic or subject area. Exam templates draw a configurable number of questions from each attached pool, so pools serve as the building blocks for exam content.
Pool Fields
| Field | Description |
|---|---|
| Name | Display name of the pool (e.g., "Aviation Weather", "B737 Systems"). |
| Description | Optional description of the pool's topic or purpose. |
| Active | Toggle controlling whether the pool can be used in exam templates. |
Question Fields
Each question within a pool has the following fields:
| Field | Description |
|---|---|
| Type | The question format: Multiple Choice, True/False, or Free Text. |
| Text | The question prompt displayed to the pilot. |
| Image | Optional image attachment to accompany the question (e.g., a chart, diagram, or instrument panel). |
| Explanation | Optional explanation shown to the pilot after grading, explaining the correct answer. |
| Active | Toggle controlling whether the question is eligible for selection. Only active questions are drawn into exams. |
Answer Choices
Multiple choice and true/false questions have answer choices with the following fields:
| Field | Description |
|---|---|
| Text | The text of the answer option. |
| Is Correct | Whether this choice is the correct answer. Multiple choices can be marked correct for multi-select questions. |
| Sort Order | Display position of the choice (randomized when the exam is generated). |
Free text questions have no predefined choices -- the pilot types their answer, and a staff member (or AI) evaluates it.
Activity Log
The pool edit page includes a paginated activity log that records every change to the pool's attributes and its questions, with timestamps and the staff member who made the change.
Exam Templates
Backoffice path: backoffice/exam-templates
Exam templates define the structure and rules of an exam. Each template pulls a specified number of random questions from one or more question pools.
Template Fields
| Field | Description |
|---|---|
| Name | Display name of the exam (e.g., "ETOPS Certification Exam", "Captain Promotion Test"). |
| Description | Optional description of the exam's purpose. |
| Duration (minutes) | Time limit in minutes. When set, a countdown timer is shown during the exam and the exam auto-submits when time expires. Leave empty for untimed exams. |
| Pass Threshold (%) | The minimum percentage score required to pass (e.g., 70 means the pilot needs 70% or higher). |
| Max Attempts | Maximum number of times a pilot can attempt the exam. Set to 0 for unlimited retries. |
| Active | Toggle controlling whether the template can be assigned to pilots. |
Pool Attachments
Each template is linked to one or more question pools. For each pool attachment, staff configure:
| Field | Description |
|---|---|
| Pool | The question pool to draw from. |
| Question Count | How many questions to randomly select from this pool for each exam instance. |
When an exam is started, the system randomly selects the configured number of questions from each pool, shuffles the overall question order, and randomizes the answer choice order within each question. This means every exam instance is unique.
Template Snapshot
When a pilot starts an exam, the system captures a snapshot of the template configuration (name, duration, pass threshold, max attempts, pool details). This ensures that changes made to a template after an exam has started do not affect in-progress or completed exams.
Assignments
Backoffice path: backoffice/exam-assignments
Exam assignments link a specific pilot to an exam template. Each assignment tracks the pilot's progress through the exam lifecycle.
Assignment Fields
| Field | Description |
|---|---|
| User | The pilot assigned to the exam. |
| Template | The exam template to be taken. |
| Assignable | Optional polymorphic reference to the entity that triggered the assignment (a License or a RankApplication). |
| Status | Current state of the assignment: Pending, In Progress, Passed, or Failed. |
| Attempts Used | Number of exam attempts the pilot has consumed so far. |
Assignment Statuses
| Status | Description |
|---|---|
| Pending | Assigned but the pilot has not started any attempt yet. |
| In Progress | The pilot has started at least one attempt. Retries may still be available. |
| Passed | The pilot has passed the exam on one of their attempts. |
| Failed | The pilot has exhausted all allowed attempts without passing. |
How Assignments Are Created
Assignments can be created in two ways:
- Manually by staff -- administrators assign an exam to a pilot from the assignments management page.
- Automatically -- when a license or rank has an
exam_template_idconfigured:- License purchase: when a pilot purchases a license that requires an exam, the system creates an assignment linking the exam template to the license. The license is not issued until the exam is passed.
- Rank promotion application: when a pilot applies for a rank that requires an exam, the system creates an assignment linking the exam template to the rank application. The promotion is not processed until the exam is passed.
The system prevents duplicate assignments -- if a pending or in-progress assignment already exists for the same user, template, and entity, the existing assignment is returned instead of creating a new one.
Taking Exams
User path: exams/{id}/take
When a pilot starts an exam, a full-screen timed experience is presented.
Exam Flow
- Start -- the pilot clicks "Start Exam" from their assignments list. The system generates the exam instance by pulling random questions from the configured pools.
- Question navigation -- a sidebar navigator shows all questions with indicators for answered and unanswered questions. Pilots can jump between questions freely.
- Answering -- pilots select choices for multiple choice and true/false questions, or type responses for free text questions. Answers are auto-saved on each interaction so no progress is lost if the browser closes.
- Timer -- for timed exams, a countdown is displayed. When the timer expires, the exam is automatically submitted with whatever answers have been saved.
- Submit -- the pilot can manually submit the exam at any time before the timer expires. Submission is final and cannot be undone.
Exam Statuses
| Status | Description |
|---|---|
| In Progress | The pilot is actively taking the exam. |
| Submitted | The exam has been submitted and is being auto-graded. |
| Pending Review | Auto-grading is complete but free text answers require staff review. |
| Graded | All answers have been graded and the final score is calculated. |
| Expired | The timer expired and the pilot had not saved any answers. |
Overdue Exam Expiry
A scheduled task runs every minute to handle overdue timed exams:
- If the pilot saved answers before the timer expired, the exam is automatically submitted for grading.
- If the pilot saved no answers at all, the exam is marked as expired and the attempt is counted as used.
Grading
Automatic Grading
Multiple choice and true/false questions are graded automatically upon submission. The system compares the pilot's selected choices against the choices marked as correct in the question pool. The answer is marked correct only if the selected set exactly matches the correct set.
Unanswered multiple choice and true/false questions are automatically marked as incorrect.
Staff Review (Free Text)
Backoffice path: backoffice/exams/{id}/grade
Free text questions cannot be auto-graded. When an exam contains free text answers, it enters the Pending Review status after auto-grading the objective questions.
Staff members review each free text answer on the grading page and can:
- Mark as correct or incorrect -- sets the
is_correctflag on the answer. - Add grading notes -- optional notes explaining the grading decision (visible to the pilot in their results).
- Use AI suggestion -- an optional AI-assisted grading suggestion is available via OpenRouter. The AI analyzes the question, the pilot's answer, and any explanation text to suggest whether the answer should be accepted. Staff always make the final decision.
Once all free text answers in an exam have been graded, the system automatically finalizes the exam score.
Score Calculation
The final score is calculated as:
score = (correct answers / total questions) x 100
The score is compared against the pass threshold from the template snapshot. If the score meets or exceeds the threshold, the exam is marked as passed; otherwise, it is marked as failed.
Results
User path: exams/{id}/results
After an exam is graded, pilots can view their results, which include:
- Overall score as a percentage.
- Pass/fail status with the threshold that was required.
- Per-question breakdown showing each question, the pilot's answer, whether it was correct, and the explanation (if one was provided in the question pool).
- Grading notes for free text questions that were reviewed by staff.
- Attempt number and how many attempts remain (if the exam allows retries).
Retries
If a pilot fails an exam and has not exhausted the maximum number of attempts, the assignment remains in In Progress status and the pilot can start a new attempt. Each new attempt generates a fresh set of randomly selected questions.
If the pilot has used all allowed attempts, the assignment transitions to Failed status and no further attempts are permitted.
Integration with Licenses, Ranks & Charter Missions
The exams system integrates directly with the license and rank career progression features.
Licenses
When a license has an exam_template_id set:
- The license store shows a "Take Exam" button instead of the purchase button. Clicking it creates an exam assignment and redirects the pilot to the exams page.
- Once the pilot passes the exam, the normal purchase button appears.
- The pilot can then purchase the license normally -- the system detects the passed exam and issues the license immediately.
- If a pilot attempts to purchase without passing first (e.g. via API), points are deducted and an exam assignment is created. The license is issued on pass via the
FulfillExamRequirementlistener.
Ranks
When a rank has an exam_template_id set:
- Once all other promotion requirements are met (hours, flights, points, landing rate), the career page shows a "Take Exam" button instead of "Apply for Promotion".
- Clicking it creates an exam assignment and redirects the pilot to the exams page.
- Once the pilot passes the exam, the "Apply for Promotion" button appears.
- The pilot can then apply normally -- the system detects the passed exam and processes the application without creating another exam assignment.
Charter Missions
When a charter mission has an exam_template_id set:
- The contract detail shows a "Take Exam" button instead of the apply button.
- Clicking it creates an exam assignment and redirects the pilot to the exams page.
- Once the pilot passes the exam, the apply button becomes available and they can submit their application normally.
Unlike licenses and ranks, passing the exam does not trigger an automatic action — it simply unlocks the ability to apply for the mission.
Discord Notifications
Exam results are posted to Discord when configured. The notification includes:
- The exam template name.
- The pilot's name.
- The score as a percentage.
- The attempt number and maximum attempts.
- A color-coded embed (green for pass, red for fail).
To enable exam notifications, set the Exams Channel in the Discord Integration settings (backoffice/settings/discord). If no channel is configured, notifications are silently skipped.
Email Notifications
The system sends email notifications at key points in the exam lifecycle:
| Trigger | Recipient | |
|---|---|---|
| Exam Assigned | An exam assignment is created. | The pilot. |
| Exam Result | An exam is graded (pass or fail). | The pilot. |
| Pending Review | An exam is submitted with free text answers awaiting staff review. | Staff members. |
Route Reference
Admin Routes
| Route | Page | Permission |
|---|---|---|
backoffice/exam-pools | Question pool list | view ExamQuestionPool |
backoffice/exam-pools/create | Create question pool | create ExamQuestionPool |
backoffice/exam-pools/{id} | Edit question pool and questions | edit ExamQuestionPool |
backoffice/exam-templates | Exam template list | view ExamTemplate |
backoffice/exam-templates/create | Create exam template | create ExamTemplate |
backoffice/exam-templates/{id} | Edit exam template | edit ExamTemplate |
backoffice/exam-assignments | Assignment list and management | view ExamAssignment |
backoffice/exams/{id}/grade | Staff grading page for free text answers | grade Exam |
User Routes
| Route | Page |
|---|---|
exams | My exams list (all assignments and their statuses). |
exams/{id}/take | Full-screen exam-taking experience. |
exams/{id}/results | View graded results and per-question breakdown. |
API Access
The exam system is fully available through the Private API v1. All endpoints require API key authentication via the X-API-Key header.
Available Endpoints
| Resource | Methods | Path |
|---|---|---|
| Question Pools | GET, POST, PUT, DELETE | /api/v1/exam-question-pools |
| Pool Questions | GET, POST, PUT, DELETE | /api/v1/exam-question-pools/{id}/questions (nested) and /api/v1/questions/{id} (shallow) |
| Exam Templates | GET, POST, PUT, DELETE | /api/v1/exam-templates |
| Exam Assignments | GET, POST, DELETE | /api/v1/exam-assignments |
| Exams | GET | /api/v1/exams |
For full details on filtering, sorting, pagination, and field descriptions, see the Exams section of the Private API v1 documentation.