StageTypeConfig
This is the key to removing all hardcoding from the frontend.
StageTypeConfig is a system-seeded collection that defines all 6 stage types. The API exposes GET /v1/stage-types (returns all active types — no plan filtering) and the frontend renders all forms dynamically from this data.
Schema
typescript
interface IStageTypeConfig {
_id: ObjectId;
key: StageTypeKey; // enum — see below
displayName: string; // e.g. "Automated Call Screening"
description: string;
icon: string; // icon name for frontend
// No plan gating on stage types — all types available on all plans.
// Usage limits on Plan/Organization handle monetization.
// Scheduling shape
schedulingConfig: {
type: 'async' | 'scheduled';
requiresStartTime: boolean;
requiresEndTime: boolean;
hasExpiryDeadline: boolean;
defaultDurationMinutes?: number;
};
// Which tools are enabled in the interview session
toolsConfig: {
whiteboard: boolean;
ide: boolean;
screenShare: boolean;
audio: boolean;
video: boolean;
aiChatbot: boolean;
webcamProctoring: boolean;
voiceInterface: boolean;
};
// What question bank type this stage uses
questionBankConfig: {
questionType: QuestionType | null; // null = no question bank (e.g. basic 1-on-1)
minQuestions: number;
maxQuestions: number;
questionsVisibleTo: 'both' | 'interviewer_only';
};
// Automation / AI behavior
automationConfig: {
hasAiInterviewer: boolean;
aiInterviewerMode: 'none' | 'qa_bot' | 'conversational' | 'coding_evaluator';
hasSendLinkToggle: boolean; // "send automated link vs. notify only"
};
// What feedback schema to apply post-stage
feedbackConfig: {
feedbackRequired: boolean;
ratingCriteria: {
key: string;
label: string;
maxScore: number;
}[];
};
// When does a stage mark itself complete?
// 'manual' — recruiter always marks completion + unlocks next stage
// 'auto_on_submission' — stage auto-marks 'completed' once candidate submits.
// Does NOT auto-unlock the next stage.
// 'auto_on_deadline' — stage is auto-marked 'expired' once deadline passes.
progressionTrigger: 'manual' | 'auto_on_submission' | 'auto_on_deadline';
order: number; // display order in dropdowns
isActive: boolean;
}
type StageTypeKey =
| 'automated_screening'
| 'technical_dsa'
| 'technical_ai_assisted'
| 'ai_conversational'
| 'live_1on1'
| 'culture_fit_hr';
type QuestionType = 'screening' | 'dsa' | 'scenario' | 'behavioral';Indexes:
{ key: 1 }— unique{ isActive: 1, order: 1 }
All 6 Stage Types
1. automated_screening
| Field | Value |
|---|---|
| displayName | Automated Call Screening |
| schedulingConfig.type | async |
| requiresStartTime | false |
| hasExpiryDeadline | false |
| toolsConfig | voiceInterface: true, all others false |
| questionBankConfig.questionType | screening |
| minQuestions / maxQuestions | 2 / 10 |
| questionsVisibleTo | both |
| automationConfig.hasAiInterviewer | true |
| aiInterviewerMode | qa_bot |
| hasSendLinkToggle | true |
| feedbackRequired | false |
| progressionTrigger | auto_on_submission |
2. technical_dsa
| Field | Value |
|---|---|
| displayName | Technical — DSA |
| schedulingConfig.type | async |
| hasExpiryDeadline | true |
| toolsConfig | ide: true, webcamProctoring: true, aiChatbot: true |
| questionBankConfig.questionType | dsa |
| minQuestions / maxQuestions | 1 / 5 |
| automationConfig.aiInterviewerMode | coding_evaluator |
| feedbackRequired | false |
| progressionTrigger | auto_on_deadline |
3. technical_ai_assisted
| Field | Value |
|---|---|
| displayName | Technical — AI Assisted |
| schedulingConfig.type | async |
| hasExpiryDeadline | true |
| toolsConfig | whiteboard: true, ide: true, audio: true, video: true, aiChatbot: true |
| questionBankConfig.questionType | scenario |
| minQuestions / maxQuestions | 1 / 3 |
| automationConfig.hasAiInterviewer | true |
| aiInterviewerMode | conversational |
| feedbackRequired | false |
| progressionTrigger | auto_on_submission |
4. ai_conversational
| Field | Value |
|---|---|
| displayName | AI Conversational |
| schedulingConfig.type | async |
| toolsConfig | voiceInterface: true, audio: true |
| questionBankConfig.questionType | scenario |
| automationConfig.hasAiInterviewer | true |
| aiInterviewerMode | conversational |
| feedbackRequired | false |
| progressionTrigger | auto_on_submission |
5. live_1on1
| Field | Value |
|---|---|
| displayName | Live 1-on-1 Interview |
| schedulingConfig.type | scheduled |
| requiresStartTime | true |
| requiresEndTime | true |
| defaultDurationMinutes | 60 |
| toolsConfig | whiteboard: true, ide: true, screenShare: true, audio: true, video: true, aiChatbot: true |
| questionBankConfig.questionType | behavioral |
| minQuestions / maxQuestions | 0 / 30 |
| questionsVisibleTo | interviewer_only |
| automationConfig.hasAiInterviewer | false |
| feedbackRequired | true |
| ratingCriteria | Technical Depth, Problem Solving, Communication, Culture Fit (all max 5) |
| progressionTrigger | manual |
6. culture_fit_hr
| Field | Value |
|---|---|
| displayName | Culture Fit / HR Round |
| schedulingConfig.type | scheduled |
| requiresStartTime | true |
| defaultDurationMinutes | 45 |
| toolsConfig | audio: true, video: true |
| questionBankConfig.questionType | behavioral |
| questionsVisibleTo | interviewer_only |
| feedbackRequired | true |
| ratingCriteria | Communication, Culture Fit, Motivation (all max 5) |
| progressionTrigger | manual |
Seed Script
src/scripts/seedStageTypes.ts — run via npm run seed:stage-types
What the Frontend Does with This Data
The frontend calls GET /v1/stage-types on app load (or job creation page mount) and caches the result in a React Query key or Context. It then uses the StageTypeConfig fields to:
StageTypeConfig field | Frontend behavior |
|---|---|
key, displayName | Stage type dropdown options |
schedulingConfig.requiresStartTime | Show/hide date + time pickers in schedule form |
schedulingConfig.hasExpiryDeadline | Show/hide expiry field |
schedulingConfig.type === 'async' | Hide date/time pickers entirely for async stages |
questionBankConfig.questionType | Which question picker panel to show |
questionBankConfig.minQuestions | Validation hint ("min X questions required") |
questionBankConfig.questionsVisibleTo === 'interviewer_only' | Show "interviewer only" notice |
automationConfig.hasSendLinkToggle | Show "send automated link" toggle |
feedbackConfig.feedbackRequired | Show "Feedback required" badge |
feedbackConfig.ratingCriteria[] | Render feedback form with dynamic criteria fields |
toolsConfig.* | Which tool panels to render in interview room |
No hardcoding
The frontend must never have if (stageTypeKey === 'live_1on1') { showDatePicker() } style logic. All conditional rendering reads from the StageTypeConfig fields listed above.