Job Board & Stage Types
Creating a Job
Jobs are created via POST /v1/jobs. Each job has a stages[] array, where every element is a fully typed stage config.
Stage Types Available
All 6 stage types are available on every plan (usage limits apply):
| Key | Display Name | Type | Question Bank |
|---|---|---|---|
automated_screening | Automated Call Screening | async | ScreeningQuestion |
technical_dsa | Technical — DSA | async | DSAProblem |
technical_ai_assisted | Technical — AI Assisted | async | ScenarioQuestion |
ai_conversational | AI Conversational | async | ScenarioQuestion |
live_1on1 | Live 1-on-1 Interview | scheduled | InterviewQuestion (private) |
culture_fit_hr | Culture Fit / HR Round | scheduled | InterviewQuestion (private) |
The frontend fetches this list from GET /v1/stage-types and never hardcodes it.
Job Creation Flow
- Recruiter opens "Create Job" page → frontend calls
GET /v1/stage-types - Stage type dropdown populates from API response (all 6 types, no plan filtering)
- For each stage added, the correct config panel is rendered based on
StageTypeConfig.questionBankConfig.questionType - Recruiter clicks "Create Job" →
POST /v1/jobs - API checks
org.featureSnapshot.maxActiveJobs→ 403 if at limit - API resolves question text →
ScreeningQuestionIDs viafindOrCreateQuestion() JobOpeningdocument created with fully typed stage configs
Job Statuses
| Status | Meaning |
|---|---|
draft | Not published — not visible to candidates |
open | Active — candidates can be invited |
closed | No longer accepting new candidates |
archived | Historical record — no new activity |
Jobs in open and draft status count toward maxActiveJobs.
Editing a Job
PATCH /v1/jobs/:id updates the job.
Note:
syncActivePipelines()— propagating stage name/type changes to active candidate pipelines — is deferred and not yet implemented. Currently,PATCH /v1/jobs/:idsaves the job without syncing pipeline stage entries. Planned behaviour when implemented: onlypendingandunlockedstage entries will be updated;in_progress,completed,expired,declined, andskippedentries will be frozen as historical records.
Job Stats (Denormalized)
Each job carries a stats sub-document updated via atomic $inc:
stats: {
totalCandidates: number; // total pipelines ever created for this job
activeCandidates: number; // pipelines with status 'active'
shortlisted: number; // pipelines with status 'shortlisted'
rejected: number; // pipelines with status 'rejected'
}Frontend: No Hardcoding
The following must never appear as constants in frontend source code:
| What to avoid hardcoding | Where to read it |
|---|---|
| Stage type names and keys | GET /v1/stage-types → key, displayName |
| Which fields show for each stage type | StageTypeConfig.schedulingConfig.* |
| Which tools are enabled | StageTypeConfig.toolsConfig.* |
| Min/max question counts | StageTypeConfig.questionBankConfig.minQuestions/maxQuestions |
| Whether questions are interviewer-only | StageTypeConfig.questionBankConfig.questionsVisibleTo |
| Whether to show send-link toggle | StageTypeConfig.automationConfig.hasSendLinkToggle |
| Whether feedback is required | StageTypeConfig.feedbackConfig.feedbackRequired |
| Feedback rating criteria and labels | StageTypeConfig.feedbackConfig.ratingCriteria[] |
Relevant Files
API-honeyhimself/src/controllers/jobOpeningController.tsAPI-honeyhimself/src/models/JobOpening.tsAPI-honeyhimself/src/controllers/stageTypeController.tsAPI-honeyhimself/src/models/StageTypeConfig.tsPeerZoom/src/components/jobs/JobForm.tsx(fetches stage types from API)PeerZoom/src/components/jobs/ScreeningQuestionsConfig.tsx(fetches questions from API)PeerZoom/src/services/stageType.service.ts