Recruitment
Recruitment
Section titled “Recruitment”Nucleus Recruitment is a built-in applicant tracking system (ATS) for managing the full hiring lifecycle — from job creation and candidate sourcing through pipeline management, interviews, offers, and automated onboarding handoff.
Overview
Section titled “Overview”What It Does
Section titled “What It Does”- Job management — create, publish, archive, and republish job listings
- Candidate pipeline — move candidates through customisable stages (Sourced → Applied → Phone Screen → Interview → Assessment → Offer → Hired)
- AI scoring — automatically score applicants against job requirements using keyword matching and LLM analysis
- Communication — email candidates directly from Nucleus with templates and merge fields
- Interview scheduling — book interviews with Google Calendar integration and self-scheduling links
- Collaborative evaluation — structured scorecards, independent scoring, team comments
- Careers page — public job listings page (no auth required) with embedded application forms
- Reporting — pipeline conversion, time-to-hire, source effectiveness, team productivity
- Onboarding handoff — hired candidates automatically become employees with onboarding checklists
How It Fits in Nucleus
Section titled “How It Fits in Nucleus”Recruitment sits between external candidate sourcing and the existing People + Onboarding tools:
External Sources Nucleus Recruitment Nucleus People/Onboarding───────────────── ─────────────────── ─────────────────────────LinkedIn Recruiter ─→ Candidate enters pipeline ─→ Hired candidate becomesSeek / Indeed ─→ Moves through stages ─→ a user with onboardingCareers page ─→ Interviews & evaluation ─→ template assignedEmployee referrals ─→ Offer → Contract → Hire ─→ Appears in People directoryCore Concepts
Section titled “Core Concepts”A job represents an open position. Jobs have:
| Field | Description |
|---|---|
| Title | The role being hired for (e.g., “Delivery Manager (Squad)“) |
| Department | Team or business unit (e.g., “Websites & Campaigns”) |
| Location | Office location + workplace type (on-site / hybrid / remote) |
| Employment type | Full-time, part-time, contract, casual |
| Salary range | Optional min/max with currency |
| Description | Rich text job description (responsibilities, requirements, benefits) |
| Pipeline template | Which hiring stages to use for this job |
| Hiring team | Recruiter, hiring manager, and interviewers |
| Status | Draft, Published, On Hold, Closed, Filled |
| Keywords | Searchable tags for the job (used in AI scoring) |
Jobs can be archived and republished — useful for recurring roles.
Candidates
Section titled “Candidates”A candidate is a person in the recruitment system. Candidates exist independently of jobs — one candidate can apply to multiple positions.
| Field | Description |
|---|---|
| Name | First and last name |
| Primary contact email | |
| Phone | Phone number |
| Location | City/country |
| Summary | Brief profile summary |
| Resume | Uploaded file (PDF, DOCX) stored in R2 |
| Source | How they entered the system (applied, sourced, referral, agency) |
| Tags | Free-form labels for filtering |
| Talent pool | Whether they’ve opted in to be retained for future roles |
Applications
Section titled “Applications”An application links a candidate to a job. It tracks:
- Current pipeline stage
- Status (active, rejected, withdrawn, hired)
- AI match score
- Source channel (which job board or link they came through)
- All stage transitions with timestamps
Pipeline Stages
Section titled “Pipeline Stages”Each job uses a pipeline template defining the stages candidates move through. The default pipeline:
| Stage | Type | Description |
|---|---|---|
| Sourced | Entry | Added by recruiter or via LinkedIn Recruiter |
| Applied | Entry | Submitted an application (system stage, always exists) |
| Phone Screen | Screening | Initial phone call — salary expectations, availability, basic fit |
| Interview | Evaluation | Formal interview with hiring manager or panel |
| Assessment | Evaluation | Take-home test, technical challenge, or skills assessment |
| Offer | Decision | Offer extended, negotiation (system stage, always exists) |
| Hired | Terminal | Offer accepted, triggers onboarding (system stage, always exists) |
Pipeline templates are customisable per department or role type. System stages (Applied, Offer, Hired) cannot be removed.
Rejection is a status within any stage, not a separate stage. When rejecting, record the stage and reason.
Key Features
Section titled “Key Features”AI Candidate Scoring
Section titled “AI Candidate Scoring”Every application receives an AI match score (0–100) based on how well the candidate matches the job requirements.
How scoring works:
- Job requirements extraction — when a job is created, requirements are tagged as Required (P3), Preferred (P2), or Optional (P1)
- Candidate profile analysis — resume text + profile data is parsed for skills, experience, education
- Rule-based matching — weighted scoring:
- Each required skill matched = +10 points
- Each preferred skill matched = +5 points
- Each optional skill matched = +2 points
- Experience level alignment = +15 points
- Education match = +10 points
- Normalised to 0–100
- LLM summary (optional) — Claude API generates a plain-English summary of strengths and gaps, similar to Workable’s “How [candidate] matches this job” panel
Display: Score shown as a badge on the candidate card (colour-coded: green 70+, amber 40–69, red 0–39) with an expandable panel showing matched/unmatched criteria.
Careers Page
Section titled “Careers Page”A public-facing careers page accessible without authentication:
- Lists all published jobs with search and department/location filters
- Individual job pages with description, requirements, benefits, and “Apply” button
- Application form: name, email, phone, resume upload, optional cover letter and summary
- SEO-optimised with JSON-LD
JobPostingstructured data for Google Jobs indexing - Source tracking via URL parameters (
?source=seek,?source=linkedin) - Mobile-responsive design
- Privacy collection notice and talent pool opt-in checkbox
URL: https://app.nucleus.fast/careers/* (public routes within the app, auth middleware skipped)
Communication
Section titled “Communication”Email candidates directly from Nucleus:
- Templates with merge fields:
{{candidate.first_name}},{{job.title}},{{interview.date}},{{company.name}}, etc. - Stage-triggered templates: automatic “thank you for applying” on application, customisable templates for each stage transition
- Communication log: every email sent/received is logged on the candidate profile
- Bulk actions: send rejection emails to multiple candidates at once
- Template library: pre-built templates for common scenarios (phone screen invite, interview confirmation, rejection, offer)
Interview Scheduling
Section titled “Interview Scheduling”- Google Calendar integration — sync interviewer availability, create calendar events with Google Meet links
- Self-scheduling links — generate a unique link for the candidate to pick from available time slots
- Interview types — phone, video, in-person, panel
- Reminders — automated email reminders 24h and 1h before
- Interview kits — package sent to interviewers with candidate resume, scorecard criteria, suggested questions
- Reschedule — candidates can reschedule up to 24h before without recruiter involvement
Collaborative Evaluation
Section titled “Collaborative Evaluation”Structured scorecards for consistent, bias-reduced evaluation:
- Scorecard templates per pipeline stage with 4–6 role-specific criteria
- Rating scale — 1 (Strong No) to 5 (Strong Yes) with behavioural anchors
- Independent submission — interviewers submit scorecards before seeing others’ feedback
- Locked after submission — prevents editing after viewing others’ scores
- Debrief view — aggregate scores, areas of agreement/disagreement, overall recommendations
- Comments — team members can @mention colleagues on candidate profiles
Employee Referrals
Section titled “Employee Referrals”Internal referral portal for employees:
- Submit a referral (candidate name, email, resume, which job, relationship to referrer)
- Track referral status: Submitted → Under Review → Interviewing → Hired/Not Selected
- Referral attribution maintained through the full pipeline
- Referral dashboard for employees to see their history
- Reward tracking: eligibility milestones, payment status
Reporting
Section titled “Reporting”- Candidates by stage per job (funnel visualisation)
- Stage conversion rates (% passing from each stage to the next)
- Time in stage (identify bottlenecks)
- Pipeline velocity (candidates moving per week)
- Time to fill (days from job opened to offer accepted)
- Time to hire (days from candidate entering pipeline to hire)
- Time to first action (days from application to first recruiter action)
- Applications by source (job boards, careers page, referrals, sourced)
- Hires by source (which sources produce actual hires, not just volume)
- Source effectiveness ratio (hires/applications per source)
- Cost per hire by source
- Activities per team member (emails sent, interviews conducted, candidates moved)
- Evaluations submitted (scorecard completion rate)
- Average time to action
Integration Points
Section titled “Integration Points”Onboarding (Automatic Handoff)
Section titled “Onboarding (Automatic Handoff)”When a candidate reaches the Hired stage:
- System creates a
usersrecord with candidate’s name, email, job title, start date onboarding_template_idis set based on the job’s department/role mappingaccess_leveldefaults toemployee(admin can adjust)- Candidate record is linked to the new user record
- Onboarding module automatically picks them up — checklist appears
- Welcome email sent with pre-start information
Contracts
Section titled “Contracts”When an offer is extended:
- Generate an employment contract from a template (via Contracts tool)
- Track contract status (sent, viewed, signed) on the candidate profile
- Offer acceptance can be gated on contract signature
- Signed contract stored in Documents for the employee record
People Directory
Section titled “People Directory”- Hired candidates appear in the People directory with their start date
- “Upcoming starters” view shows hired candidates pre-start-date
- Recruitment data (source, interview scores, hire date) available on the person profile for reference
Google Calendar
Section titled “Google Calendar”- Interview scheduling creates Google Calendar events
- Interviewer availability read from Google Calendar free/busy data
- Self-scheduling links use available slots from connected calendars
Privacy & Compliance
Section titled “Privacy & Compliance”Australian Privacy Act Requirements
Section titled “Australian Privacy Act Requirements”Recruitment involves collecting personal information from external candidates. Nucleus must comply with the Privacy Act 1988 (Cth) and the 2024 amendments:
| Requirement | Implementation |
|---|---|
| Privacy collection notice | Displayed on the application form before submission |
| Purpose limitation | Only collect information reasonably necessary for recruitment |
| Consent for retention | Talent pool opt-in checkbox (not pre-checked) |
| Automated decision disclosure | AI scoring disclosure on application form (required from Dec 2026) |
| Right of access | Candidate can request their data via email |
| Right of correction | Candidate can request corrections |
| Data security | Encryption at rest (D1) and in transit (HTTPS). Sensitive fields application-encrypted |
Data Retention Policy
Section titled “Data Retention Policy”| Data | Retention | Action |
|---|---|---|
| Active candidates | Retained while application is active | — |
| Rejected candidates (no talent pool consent) | 12 months after rejection | Anonymise or delete |
| Rejected candidates (talent pool opt-in) | Until consent withdrawn | Provide opt-out mechanism |
| Hired candidates | Data transitions to employee record | Retained per employment law |
| Withdrawn candidates | 6 months | Anonymise or delete |
A scheduled job runs monthly to flag candidate records due for anonymisation based on these rules.
Consent Tracking
Section titled “Consent Tracking”Every consent is recorded with:
- Type (application, talent pool, marketing)
- Granted timestamp
- IP address (audit trail)
- Exact consent text shown
- Withdrawn timestamp (if applicable)
Permissions
Section titled “Permissions”| Access Level | Can View | Can Update | Can Manage |
|---|---|---|---|
| Executive | All jobs, all candidates | Full pipeline management | Create/archive jobs, manage settings |
| Head | All jobs, all candidates | Move candidates, submit evaluations | Create/archive jobs |
| Manager | Own jobs + squad member jobs | Move candidates, submit evaluations | Create jobs for own team |
| Lead | Referral portal only | Submit referrals | — |
| Employee | Referral portal only | Submit referrals | — |
Data scoping: Managers see jobs where they are the hiring manager or a member of the hiring team. Leads and employees only see the referral submission portal.
Database Schema
Section titled “Database Schema”Core Tables
Section titled “Core Tables”-- Jobs (open positions)CREATE TABLE IF NOT EXISTS recruitment_jobs ( id TEXT PRIMARY KEY, title TEXT NOT NULL, department TEXT, job_code TEXT, description TEXT NOT NULL, -- rich text (HTML) requirements TEXT, -- rich text (HTML) benefits TEXT, -- rich text (HTML) workplace_type TEXT DEFAULT 'hybrid', -- 'on_site', 'hybrid', 'remote' location TEXT, employment_type TEXT DEFAULT 'full_time', -- 'full_time', 'part_time', 'contract', 'casual' experience_level TEXT, -- 'entry', 'mid', 'senior', 'lead', 'executive' salary_min REAL, salary_max REAL, salary_currency TEXT DEFAULT 'AUD', keywords TEXT, -- JSON array of searchable tags pipeline_template_id TEXT REFERENCES recruitment_pipeline_templates(id), status TEXT NOT NULL DEFAULT 'draft', -- 'draft', 'published', 'paused', 'closed', 'filled' published_at TEXT, closed_at TEXT, created_by TEXT REFERENCES users(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')));
-- Hiring team members for each jobCREATE TABLE IF NOT EXISTS recruitment_hiring_team ( id TEXT PRIMARY KEY, job_id TEXT NOT NULL REFERENCES recruitment_jobs(id) ON DELETE CASCADE, user_id TEXT NOT NULL REFERENCES users(id), role TEXT NOT NULL, -- 'recruiter', 'hiring_manager', 'interviewer', 'coordinator' created_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(job_id, user_id));
-- Candidates (people in the recruitment system)CREATE TABLE IF NOT EXISTS recruitment_candidates ( id TEXT PRIMARY KEY, first_name TEXT NOT NULL, last_name TEXT NOT NULL, email TEXT NOT NULL, phone TEXT, location TEXT, summary TEXT, -- brief profile summary headline TEXT, -- e.g. "Senior Engineer at Acme" resume_url TEXT, -- R2 file URL resume_text TEXT, -- extracted plain text from resume linkedin_url TEXT, source TEXT DEFAULT 'applied', -- 'applied', 'sourced', 'referral', 'agency', 'careers_page' tags TEXT, -- JSON array of tags talent_pool INTEGER DEFAULT 0, -- opted in to talent pool user_id TEXT REFERENCES users(id), -- set when hired and user record created created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')));
CREATE UNIQUE INDEX IF NOT EXISTS idx_recruitment_candidates_email ON recruitment_candidates(email);
-- Applications (candidate × job)CREATE TABLE IF NOT EXISTS recruitment_applications ( id TEXT PRIMARY KEY, job_id TEXT NOT NULL REFERENCES recruitment_jobs(id), candidate_id TEXT NOT NULL REFERENCES recruitment_candidates(id), current_stage_id TEXT REFERENCES recruitment_pipeline_stages(id), status TEXT NOT NULL DEFAULT 'active', -- 'active', 'rejected', 'withdrawn', 'hired' rejection_reason TEXT, rejection_stage_id TEXT, -- stage at which rejected source_channel TEXT, -- specific channel: 'seek', 'linkedin', 'indeed', 'careers_page', 'referral' ai_score INTEGER, -- 0-100 match score ai_summary TEXT, -- LLM-generated match summary ai_criteria TEXT, -- JSON: matched/unmatched criteria applied_at TEXT NOT NULL DEFAULT (datetime('now')), moved_at TEXT, -- last stage transition hired_at TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(job_id, candidate_id));
CREATE INDEX IF NOT EXISTS idx_recruitment_applications_job ON recruitment_applications(job_id);CREATE INDEX IF NOT EXISTS idx_recruitment_applications_candidate ON recruitment_applications(candidate_id);CREATE INDEX IF NOT EXISTS idx_recruitment_applications_status ON recruitment_applications(status);Pipeline Tables
Section titled “Pipeline Tables”-- Pipeline templates (e.g., "Engineering", "Sales", "General")CREATE TABLE IF NOT EXISTS recruitment_pipeline_templates ( id TEXT PRIMARY KEY, name TEXT NOT NULL, is_default INTEGER DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now')));
-- Pipeline stages (ordered)CREATE TABLE IF NOT EXISTS recruitment_pipeline_stages ( id TEXT PRIMARY KEY, pipeline_template_id TEXT NOT NULL REFERENCES recruitment_pipeline_templates(id) ON DELETE CASCADE, name TEXT NOT NULL, stage_type TEXT NOT NULL, -- 'entry', 'screening', 'evaluation', 'decision', 'terminal' position INTEGER NOT NULL, is_system INTEGER DEFAULT 0, -- locked stages: Applied, Offer, Hired color TEXT, -- hex colour for UI created_at TEXT NOT NULL DEFAULT (datetime('now')));
CREATE INDEX IF NOT EXISTS idx_recruitment_pipeline_stages_template ON recruitment_pipeline_stages(pipeline_template_id, position);
-- Stage transition history (audit trail)CREATE TABLE IF NOT EXISTS recruitment_stage_transitions ( id TEXT PRIMARY KEY, application_id TEXT NOT NULL REFERENCES recruitment_applications(id) ON DELETE CASCADE, from_stage_id TEXT, to_stage_id TEXT NOT NULL REFERENCES recruitment_pipeline_stages(id), moved_by TEXT REFERENCES users(id), note TEXT, moved_at TEXT NOT NULL DEFAULT (datetime('now')));
CREATE INDEX IF NOT EXISTS idx_recruitment_transitions_application ON recruitment_stage_transitions(application_id);Communication Tables
Section titled “Communication Tables”-- Email templates with merge fields and categoriesCREATE TABLE IF NOT EXISTS recruitment_email_templates ( id TEXT PRIMARY KEY, name TEXT NOT NULL, subject TEXT NOT NULL, -- supports {{merge.fields}} body TEXT NOT NULL, -- rich text with merge fields category TEXT NOT NULL DEFAULT 'general', -- 'thank_you', 'interview', 'rejection', 'offer', 'general' is_active INTEGER NOT NULL DEFAULT 1, created_by TEXT REFERENCES users(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')));
-- Stage email rules (auto-send on stage entry)CREATE TABLE IF NOT EXISTS recruitment_stage_email_rules ( id TEXT PRIMARY KEY, stage_id TEXT NOT NULL REFERENCES recruitment_pipeline_stages(id) ON DELETE CASCADE, template_id TEXT NOT NULL REFERENCES recruitment_email_templates(id) ON DELETE CASCADE, trigger_on TEXT NOT NULL DEFAULT 'enter', is_active INTEGER NOT NULL DEFAULT 1, UNIQUE(stage_id, template_id));
-- Communication log (all emails sent to candidates)CREATE TABLE IF NOT EXISTS recruitment_communications ( id TEXT PRIMARY KEY, candidate_id TEXT NOT NULL REFERENCES recruitment_candidates(id), application_id TEXT REFERENCES recruitment_applications(id), template_id TEXT REFERENCES recruitment_email_templates(id), direction TEXT NOT NULL DEFAULT 'outbound', channel TEXT NOT NULL DEFAULT 'email', subject TEXT NOT NULL, body TEXT NOT NULL, to_email TEXT NOT NULL, from_email TEXT NOT NULL, gmail_message_id TEXT, status TEXT NOT NULL DEFAULT 'sent', -- 'sent', 'failed' error TEXT, sent_by TEXT REFERENCES users(id), sent_at TEXT NOT NULL DEFAULT (datetime('now')));Interview & Evaluation Tables
Section titled “Interview & Evaluation Tables”-- Interviews with Google Calendar integration and self-schedulingCREATE TABLE IF NOT EXISTS recruitment_interviews ( id TEXT PRIMARY KEY, application_id TEXT NOT NULL REFERENCES recruitment_applications(id) ON DELETE CASCADE, candidate_id TEXT NOT NULL REFERENCES recruitment_candidates(id), job_id TEXT NOT NULL REFERENCES recruitment_jobs(id), title TEXT NOT NULL, description TEXT, interview_type TEXT NOT NULL DEFAULT 'video_call', -- 'video_call', 'phone', 'in_person', 'take_home' location TEXT, scheduled_at TEXT, duration_minutes INTEGER NOT NULL DEFAULT 60, timezone TEXT NOT NULL DEFAULT 'Australia/Melbourne', status TEXT NOT NULL DEFAULT 'scheduled', -- 'scheduled', 'pending_schedule', 'completed', 'cancelled' scheduling_type TEXT NOT NULL DEFAULT 'manual', -- 'manual', 'self_schedule' self_schedule_token TEXT UNIQUE, self_schedule_expires_at TEXT, self_schedule_slots TEXT, -- JSON array of available slots google_calendar_event_id TEXT, reminder_24h_sent INTEGER NOT NULL DEFAULT 0, reminder_1h_sent INTEGER NOT NULL DEFAULT 0, scheduled_by TEXT REFERENCES users(id), completed_at TEXT, cancelled_at TEXT, cancelled_by TEXT REFERENCES users(id), cancel_reason TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')));
-- Interview participants with feedback and ratingCREATE TABLE IF NOT EXISTS recruitment_interview_participants ( id TEXT PRIMARY KEY, interview_id TEXT NOT NULL REFERENCES recruitment_interviews(id) ON DELETE CASCADE, user_id TEXT NOT NULL REFERENCES users(id), role TEXT NOT NULL DEFAULT 'interviewer', response_status TEXT DEFAULT 'pending', feedback TEXT, rating INTEGER, created_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(interview_id, user_id));
-- Candidate comments (team discussion with @mentions and threading)CREATE TABLE IF NOT EXISTS recruitment_comments ( id TEXT PRIMARY KEY, candidate_id TEXT NOT NULL REFERENCES recruitment_candidates(id) ON DELETE CASCADE, application_id TEXT REFERENCES recruitment_applications(id), author_id TEXT NOT NULL REFERENCES users(id), content TEXT NOT NULL, mentions TEXT, -- JSON array of mentioned user IDs parent_id TEXT REFERENCES recruitment_comments(id) ON DELETE CASCADE, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')));Evaluation Tables
Section titled “Evaluation Tables”-- Scorecard templates (per stage or job)CREATE TABLE IF NOT EXISTS recruitment_scorecard_templates ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT, pipeline_stage_id TEXT REFERENCES recruitment_pipeline_stages(id) ON DELETE SET NULL, job_id TEXT REFERENCES recruitment_jobs(id) ON DELETE SET NULL, is_default INTEGER DEFAULT 0, created_by TEXT REFERENCES users(id), created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')));
-- Criteria within a scorecard templateCREATE TABLE IF NOT EXISTS recruitment_scorecard_criteria ( id TEXT PRIMARY KEY, scorecard_template_id TEXT NOT NULL REFERENCES recruitment_scorecard_templates(id) ON DELETE CASCADE, name TEXT NOT NULL, description TEXT, category TEXT, -- 'technical', 'communication', 'culture', 'leadership' weight INTEGER NOT NULL DEFAULT 1, sort_order INTEGER NOT NULL DEFAULT 0, anchors TEXT, -- JSON: {"1": "Poor", "3": "Meets expectations", "5": "Exceptional"} created_at TEXT NOT NULL DEFAULT (datetime('now')));
-- Submitted evaluations (one per evaluator per application per interview)CREATE TABLE IF NOT EXISTS recruitment_evaluations ( id TEXT PRIMARY KEY, application_id TEXT NOT NULL REFERENCES recruitment_applications(id) ON DELETE CASCADE, evaluator_id TEXT NOT NULL REFERENCES users(id), scorecard_template_id TEXT REFERENCES recruitment_scorecard_templates(id), interview_id TEXT REFERENCES recruitment_interviews(id) ON DELETE SET NULL, recommendation TEXT, -- 'strong_yes', 'yes', 'no_decision', 'no', 'strong_no' notes TEXT, overall_score REAL, -- computed weighted average is_submitted INTEGER DEFAULT 0, submitted_at TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(application_id, evaluator_id, interview_id));
-- Individual criterion scoresCREATE TABLE IF NOT EXISTS recruitment_evaluation_scores ( id TEXT PRIMARY KEY, evaluation_id TEXT NOT NULL REFERENCES recruitment_evaluations(id) ON DELETE CASCADE, criteria_id TEXT NOT NULL REFERENCES recruitment_scorecard_criteria(id) ON DELETE CASCADE, score INTEGER, -- 1-5 evidence TEXT, -- text justification created_at TEXT NOT NULL DEFAULT (datetime('now')));Referral & Activity Tables
Section titled “Referral & Activity Tables”-- Employee referralsCREATE TABLE IF NOT EXISTS recruitment_referrals ( id TEXT PRIMARY KEY, referrer_user_id TEXT NOT NULL REFERENCES users(id), candidate_id TEXT NOT NULL REFERENCES recruitment_candidates(id), job_id TEXT REFERENCES recruitment_jobs(id), -- NULL if general referral relationship TEXT, -- 'former_colleague', 'friend', 'professional_contact' note TEXT, -- referrer's endorsement status TEXT DEFAULT 'submitted', -- 'submitted', 'reviewing', 'hired', 'rejected' created_at TEXT NOT NULL DEFAULT (datetime('now')));
-- Candidate consent tracking (privacy compliance)CREATE TABLE IF NOT EXISTS recruitment_consents ( id TEXT PRIMARY KEY, candidate_id TEXT NOT NULL REFERENCES recruitment_candidates(id) ON DELETE CASCADE, consent_type TEXT NOT NULL, -- 'application', 'talent_pool', 'marketing' consent_text TEXT NOT NULL, -- exact text shown granted_at TEXT NOT NULL DEFAULT (datetime('now')), expires_at TEXT, withdrawn_at TEXT, ip_address TEXT);
-- Activity log (audit trail for all actions)CREATE TABLE IF NOT EXISTS recruitment_activities ( id TEXT PRIMARY KEY, application_id TEXT REFERENCES recruitment_applications(id), candidate_id TEXT REFERENCES recruitment_candidates(id), job_id TEXT REFERENCES recruitment_jobs(id), actor_id TEXT REFERENCES users(id), action TEXT NOT NULL, -- 'applied', 'stage_changed', 'email_sent', 'note_added', -- 'evaluation_submitted', 'interview_scheduled', 'rejected', -- 'offer_extended', 'hired', 'referral_submitted' details TEXT, -- JSON with action-specific data created_at TEXT NOT NULL DEFAULT (datetime('now')));
CREATE INDEX IF NOT EXISTS idx_recruitment_activities_application ON recruitment_activities(application_id);CREATE INDEX IF NOT EXISTS idx_recruitment_activities_candidate ON recruitment_activities(candidate_id);CREATE INDEX IF NOT EXISTS idx_recruitment_activities_created ON recruitment_activities(created_at);Job Requirements (for AI Scoring)
Section titled “Job Requirements (for AI Scoring)”-- Job requirements (extracted from description, used for AI scoring)CREATE TABLE IF NOT EXISTS recruitment_job_requirements ( id TEXT PRIMARY KEY, job_id TEXT NOT NULL REFERENCES recruitment_jobs(id) ON DELETE CASCADE, category TEXT NOT NULL, -- 'required', 'preferred', 'optional' description TEXT NOT NULL, -- "Experience in enterprise project management" priority INTEGER NOT NULL DEFAULT 2, -- 1-5 (5 = most important) weight REAL NOT NULL DEFAULT 1.0, -- multiplier for scoring (0.1 - 5.0) position INTEGER NOT NULL);AI Scoring
Section titled “AI Scoring”The AI scoring system combines rule-based keyword matching with LLM-powered analysis:
- Resume text extraction — PDFs uploaded to R2 are parsed via
unpdfto extract text - Rule-based scoring — keywords from job requirements are matched against resume text, weighted by category (required ×3, preferred ×2, optional ×1), priority (1-5), and custom weight
- AI match summary — Workers AI (
@cf/moonshotai/kimi-k2.5) generates a structured analysis with per-requirement match decisions and confidence levels - Score storage —
ai_score(0-100),ai_summary(text),ai_criteria(JSON array), andai_scored_aton the application record
The scoring engine is in worker/lib/ai-scoring.ts and follows the same AI binding pattern as worker/routes/people.ts (binding + REST API fallback for local dev).
API Routes
Section titled “API Routes”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/jobs | List all jobs (filtered by status, department) | view |
| GET | /api/recruitment/jobs/:id | Get job details with pipeline stages and team | view |
| POST | /api/recruitment/jobs | Create a new job | manage |
| PUT | /api/recruitment/jobs/:id | Update job details | update |
| PUT | /api/recruitment/jobs/:id/status | Change job status (publish, close, archive) | manage |
| GET | /api/recruitment/jobs/:id/candidates | List candidates for a job by stage | view |
| GET | /api/recruitment/jobs/:id/stats | Pipeline stats for a job | view |
Candidates
Section titled “Candidates”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/candidates | Search candidates (name, email, tags, stage) | view |
| GET | /api/recruitment/candidates/:id | Full candidate profile with all applications | view |
| POST | /api/recruitment/candidates | Create candidate (manual add or sourced) | update |
| PUT | /api/recruitment/candidates/:id | Update candidate details | update |
| DELETE | /api/recruitment/candidates/:id | Anonymise candidate (privacy compliance) | manage |
Applications
Section titled “Applications”| Method | Path | Description | Permission |
|---|---|---|---|
| POST | /api/recruitment/applications | Create application (candidate applies to job) | update |
| PUT | /api/recruitment/applications/:id/stage | Move candidate to a new stage | update |
| PUT | /api/recruitment/applications/:id/reject | Reject candidate with reason | update |
| PUT | /api/recruitment/applications/:id/hire | Mark as hired (triggers onboarding) | manage |
| GET | /api/recruitment/applications/:id/timeline | Full activity timeline | view |
Email Templates
Section titled “Email Templates”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/email-templates | List all email templates | view |
| GET | /api/recruitment/email-templates/:id | Get template by ID | view |
| POST | /api/recruitment/email-templates | Create template | manage |
| PUT | /api/recruitment/email-templates/:id | Update template | manage |
| DELETE | /api/recruitment/email-templates/:id | Delete template | manage |
| POST | /api/recruitment/email-templates/:id/preview | Preview with merge fields resolved | view |
Communication
Section titled “Communication”| Method | Path | Description | Permission |
|---|---|---|---|
| POST | /api/recruitment/candidates/:id/send-email | Send email to candidate | update |
| GET | /api/recruitment/candidates/:id/communications | Get communication log | view |
| POST | /api/recruitment/bulk-email | Send email to multiple candidates | manage |
Stage Email Rules
Section titled “Stage Email Rules”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/pipeline-templates/:id/email-rules | Get rules for a pipeline | view |
| PUT | /api/recruitment/pipeline-templates/:id/email-rules | Update rules for a pipeline | manage |
Interviews
Section titled “Interviews”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/interviews | List interviews (filterable by status) | view |
| GET | /api/recruitment/interviews/:id | Get interview details with participants | view |
| POST | /api/recruitment/interviews | Schedule an interview (creates Calendar event) | update |
| PUT | /api/recruitment/interviews/:id | Update interview details | update |
| PUT | /api/recruitment/interviews/:id/cancel | Cancel interview (deletes Calendar event) | update |
| PUT | /api/recruitment/interviews/:id/complete | Mark interview as completed | update |
| PUT | /api/recruitment/interviews/:id/participants/:userId/feedback | Submit participant feedback and rating | update |
Comments
Section titled “Comments”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/candidates/:id/comments | List comments for a candidate | view |
| POST | /api/recruitment/candidates/:id/comments | Add a comment (with @mentions, threading) | update |
| PUT | /api/recruitment/comments/:id | Update a comment (author only) | update |
| DELETE | /api/recruitment/comments/:id | Delete a comment | manage |
Scorecard Templates
Section titled “Scorecard Templates”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/scorecard-templates | List all scorecard templates | view |
| GET | /api/recruitment/scorecard-templates/:id | Get template with criteria | view |
| POST | /api/recruitment/scorecard-templates | Create scorecard template | manage |
| PUT | /api/recruitment/scorecard-templates/:id | Update template and criteria | manage |
| DELETE | /api/recruitment/scorecard-templates/:id | Delete template | manage |
Evaluations
Section titled “Evaluations”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/applications/:id/evaluations | List evaluations for application (independent scoring) | view |
| GET | /api/recruitment/evaluations/:id | Get evaluation with criterion scores | view |
| POST | /api/recruitment/applications/:id/evaluations | Create new evaluation (draft) | update |
| PUT | /api/recruitment/evaluations/:id | Update draft evaluation | update |
| PUT | /api/recruitment/evaluations/:id/submit | Submit evaluation (validates, computes score, locks) | update |
| DELETE | /api/recruitment/evaluations/:id | Delete draft evaluation (own only) | update |
| GET | /api/recruitment/applications/:id/debrief | Aggregate debrief summary | view |
Careers Page (Public — No Auth)
Section titled “Careers Page (Public — No Auth)”| Method | Path | Description |
|---|---|---|
| GET | /api/careers/jobs | List published jobs |
| GET | /api/careers/jobs/:id | Get job details for public view |
| POST | /api/careers/apply | Submit application (with file upload) |
| GET | /api/careers/schedule/:token | Get self-scheduling interview details and available slots |
| POST | /api/careers/schedule/:token | Confirm a self-scheduled interview time |
Referrals
Section titled “Referrals”| Method | Path | Description | Permission |
|---|---|---|---|
| POST | /api/recruitment/referrals | Submit a referral | view (any user) |
| GET | /api/recruitment/referrals/mine | Get my referral history | view (any user) |
| GET | /api/recruitment/referrals | List all referrals | manage |
Reporting
Section titled “Reporting”| Method | Path | Description | Permission |
|---|---|---|---|
| GET | /api/recruitment/reports/pipeline | Pipeline funnel data | view |
| GET | /api/recruitment/reports/time-to-hire | Time metrics | view |
| GET | /api/recruitment/reports/sources | Source effectiveness | view |
| GET | /api/recruitment/reports/team | Team productivity | manage |
UI Structure
Section titled “UI Structure”Routes
Section titled “Routes”| Route | Description | Phase |
|---|---|---|
/recruitment | Jobs list (default view) — all open positions with pipeline stage counts | 1 |
/recruitment/jobs/$jobId | Job detail — candidate pipeline view (list by stage) | 1 |
/recruitment/jobs/$jobId/edit | Edit job details | 1 |
/recruitment/candidates | Candidate database — search and filter across all candidates | 1 |
/recruitment/candidates/$candidateId | Candidate profile — applications, timeline, communications, comments | 1 |
/recruitment/templates | Email template management (create, edit, delete) | 2 |
/recruitment/interviews | Interview list with status tabs (Upcoming, Completed, Cancelled) | 2 |
/recruitment/scorecards | Scorecard template management (create, edit, delete criteria) | 3 |
/careers | Public careers page — published job listings | 1 |
/careers/jobs/$jobId | Public job detail and application form | 1 |
/careers/schedule/$token | Public self-scheduling page for candidates | 2 |
Component Directory
Section titled “Component Directory”src/components/recruitment/├── jobs-list.tsx # Jobs table with status badges and pipeline counts├── job-form.tsx # Create/edit job form (Sheet)├── job-edit.tsx # Edit job page├── job-pipeline.tsx # Candidates grouped by pipeline stage├── candidate-search.tsx # Searchable candidate table├── candidate-profile.tsx # Full candidate detail view (Timeline, Communications, Comments tabs)├── add-candidate-drawer.tsx # Add candidate to job (Sheet)├── email-template-list.tsx # Email template management with category tabs├── email-template-form.tsx # Create/edit template with merge field toolbar (Sheet)├── email-composer.tsx # Send email with template selection and preview (Sheet)├── communication-log.tsx # Chronological email history with expandable body├── stage-email-rules.tsx # Configure auto-send emails per pipeline stage (Sheet)├── schedule-interview-sheet.tsx # Schedule interview with participant selection (Sheet)├── interview-list.tsx # Interview list with status tabs├── candidate-comments.tsx # Threaded comments with @mentions and replies├── bulk-email-composer.tsx # Send email to multiple candidates (Sheet)├── scorecard-template-list.tsx # Scorecard template management page├── scorecard-template-form.tsx # Create/edit scorecard with criteria editor (Sheet)├── evaluation-form.tsx # Scorecard submission form with 1-5 scoring (Sheet)├── evaluation-list.tsx # Evaluations tab on candidate profile├── evaluation-detail.tsx # Full evaluation view with score breakdown (Sheet)└── debrief-summary.tsx # Aggregate evaluation debrief with averages (Sheet)Phased Delivery Roadmap
Section titled “Phased Delivery Roadmap”Phase 1 — Core ATS (MVP) ✓
Section titled “Phase 1 — Core ATS (MVP) ✓”Goal: Replace Workable for basic hiring workflow. Create jobs, receive applications, manage pipeline, hire candidates.
Scope:
- Database migration: core tables (jobs, candidates, applications, pipeline templates/stages, activities)
- API routes: jobs CRUD, candidates CRUD, applications CRUD, stage transitions
- Jobs list page with status badges and pipeline stage counts
- Job detail page with candidate pipeline view (drag-and-drop Kanban by stage)
- Candidate profile page with resume viewer and activity timeline
- Move candidates between stages with notes
- Reject candidates with reason
- Default pipeline template with seed data (Sourced → Applied → Phone Screen → Interview → Assessment → Offer → Hired)
- Hiring team assignment per job
- Basic candidate search (name, email, job, stage)
- Source tracking on applications
- Careers page: public job listings and application form with resume upload (R2)
- Permission setup: tool_permissions seed data for all access levels
- Sidebar entry, route titles, header actions
- Mark as hired: create user record, assign onboarding template, link to People
Phase 2 — Communication & Scheduling ✓
Section titled “Phase 2 — Communication & Scheduling ✓”Goal: Handle all candidate communication and interview coordination inside Nucleus.
Scope:
- Email templates with merge fields (
{{candidate.first_name}},{{job.title}}, etc.) - Send email to candidate from profile (with template selection)
- Communication log on candidate profile (expandable email history)
- Stage-triggered automatic emails (configurable per pipeline stage)
- Interview scheduling with Google Calendar integration (event creation with attendees)
- Self-scheduling links for candidates (public token-based page)
- Interview reminders (cron trigger every 15 min, sends 24h and 1h before)
- Interview list page with status tabs
- Bulk email (send to multiple candidates with personalised merge fields)
- Candidate comments (threaded with @mentions and replies)
Phase 3 — Evaluation & Collaboration ✓
Section titled “Phase 3 — Evaluation & Collaboration ✓”Goal: Structured, bias-reduced evaluation with scorecards and team collaboration.
Scope:
- Scorecard templates with criteria (per stage, per job, or global)
- Scorecard submission by interviewers (1–5 rating per criterion + evidence)
- Independent scoring (hidden until user submits, then revealed)
- Debrief summary view (aggregate scores, areas of agreement/disagreement)
- Interview kits (candidate summary + scorecard sent to interviewers)
- Overall recommendation per evaluator (Strong No → Strong Yes)
- Evaluation status tracking on candidate profile Evaluations tab
Phase 4 — AI Scoring, LinkedIn Enrichment & Talent Pool
Section titled “Phase 4 — AI Scoring, LinkedIn Enrichment & Talent Pool”Goal: AI-powered candidate screening, LinkedIn profile enrichment, and a persistent talent database.
Scope:
- Job requirements extraction (required/preferred/optional tagging with weight and priority)
- AI-powered requirements extraction from job descriptions
- Resume text extraction from uploaded PDFs (via
unpdf) - Rule-based scoring engine (weighted keyword matching with category multipliers)
- AI match score displayed on candidate cards and pipeline views
- LLM-powered match summary via Workers AI (
@cf/moonshotai/kimi-k2.5) - Criteria breakdown panel (matched/unmatched requirements with confidence levels)
- Score All button for batch scoring all applications on a job
- Talent pool: toggle candidates into talent pool for future roles
- Talent pool search and filtering with resume text search
- Duplicate candidate detection (match by email and name)
- Job requirements editor with AI extraction and weight/priority controls
- LinkedIn profile enrichment via Scrapin.io API
- Auto-enrich on application submission (when
linkedin_urlis provided) - On-demand “Enrich” button on candidate profile
- Stores work history, education, skills, certifications on candidate record
- “LinkedIn” tab on candidate profile showing enriched data
- Enrichment data fed into AI scoring for richer matching
- Connection card in Settings > Connections for API key management
- Auto-enrich on application submission (when
Phase 5 — Reporting & Analytics
Section titled “Phase 5 — Reporting & Analytics”Goal: Data-driven recruitment insights.
Scope:
- Pipeline funnel report (conversion rates per stage)
- Time-to-hire and time-to-fill metrics
- Time-in-stage analysis (bottleneck identification)
- Source effectiveness report (applications vs hires by source)
- Team productivity dashboard (activities per team member)
- Offer acceptance rate tracking
- Cost per hire tracking
- CSV/PDF export for reporting
- Dashboard widget: open positions + pipeline summary
Phase 6 — Privacy, Compliance & Referrals
Section titled “Phase 6 — Privacy, Compliance & Referrals”Goal: Australian Privacy Act compliance and employee referral program.
Scope:
- Privacy collection notice on application form
- Consent tracking (application, talent pool, marketing)
- AI scoring disclosure (required from Dec 2026)
- Data retention automation (monthly job to flag/anonymise expired records)
- Candidate data access/deletion request handling
- Employee referral portal (submit, track status)
- Referral attribution through pipeline
- Referral dashboard for employees
Phase 7 — Advanced Features (Future)
Section titled “Phase 7 — Advanced Features (Future)”Goal: Job board integrations, advanced workflows, and contract automation.
Scope:
- SEEK API integration (direct job posting)
- Indeed Apply integration (webhook for applications)
- LinkedIn Recruiter System Connect
- SMS communication (Twilio integration)
- Offer letter generation with e-signature (via Contracts tool)
- Custom pipeline templates per department
- Kanban drag-and-drop pipeline view
- Advanced search: full-text candidate search across resumes
- Candidate self-service portal (view application status)
- Interview scheduling with panel availability matching
- Google Jobs structured data on careers page
- Automated stage transitions (e.g., auto-advance after scorecard threshold)
Appendix: Workable Feature Mapping
Section titled “Appendix: Workable Feature Mapping”How Nucleus Recruitment maps to the Workable features observed in the reference screenshots:
| Workable Feature | Nucleus Equivalent | Phase |
|---|---|---|
| Jobs list with pipeline stage counts | /recruitment jobs list | 1 |
| Job editor (title, dept, location, description, salary) | Job form with all fields | 1 |
| Application form builder (mandatory/optional/off) | Simplified: fixed form with core fields | 1 |
| Candidate pipeline view (stages as tabs) | Job detail page with stage-grouped list | 1 |
| Candidate profile (summary, resume, contact) | Candidate profile page | 1 |
| AI Screening Assistant (score + criteria) | AI score badge + criteria breakdown | 4 |
| Communication panel (send email, templates) | Email composer with templates | 2 |
| Activity stream (global feed) | Activity timeline per candidate | 1 |
| Candidate database search | Candidate search page | 1 |
| Premium Job Boards (LinkedIn, Seek, Indeed) | SEEK integration (syndicates to LinkedIn) — see SEEK Integration | 7 |
| Reports (team productivity) | Reporting dashboard | 5 |
| Onboarding trigger | Auto-create user + assign onboarding template | 1 |
| Work calendar (interviews) | Google Calendar integration | 2 |
| Candidate tags & filters | Tags + search/filter UI | 1 |
| ”Find Candidates” (AI sourcing) | Talent pool search | 4 |
| Texting candidates | SMS via Twilio | 7 |
Architecture Decisions
Section titled “Architecture Decisions”| Decision | Choice | Rationale |
|---|---|---|
| Careers page hosting | Public route within the app (/careers/*) | Single deployment, shared API, public routes skip auth middleware |
| Resume storage | Shared R2 bucket with /recruitment/ prefix | Simpler config, one bucket to manage, existing infrastructure |
| Email sending | Reuse announcements email infrastructure | Already working, less config, shared sending setup |
| Calendar provider | Google Calendar only | Google Workspace is the company standard, no Outlook needed |
| Contract integration | Manual (recruiter clicks “Create Contract”) | More flexible, avoids premature drafts, cleaner workflow |
| Job board distribution | Via SEEK, which syndicates to LinkedIn | SEEK’s syndication is simpler than maintaining two separate API integrations |
SEEK Integration
Section titled “SEEK Integration”Nucleus integrates with the SEEK Job Posting API to distribute job ads to SEEK (and optionally LinkedIn via SEEK’s syndication feature).
- Go to Settings → Connections
- Find the SEEK connection card
- Enter your SEEK API Access Token and Advertiser ID
- Toggle LinkedIn Syndication on/off as needed
- Click Save
How It Works
Section titled “How It Works”| Event | Action |
|---|---|
Job transitions to published | Posts a new ad to SEEK (applies URL set to /careers/{jobId}) |
| Published job fields updated | Patches the existing SEEK ad with updated content |
Job transitions to paused, closed, or filled | Expires/closes the SEEK ad |
The apply URL on all SEEK and LinkedIn listings points to Nucleus’s hosted careers page (/careers/{jobId}), so all applications are tracked centrally.
Status Indicators
Section titled “Status Indicators”On the job detail page, a SEEK badge appears in the header when the job has an active SEEK ad. If there’s a sync error (e.g. invalid credentials), a SEEK Error badge appears instead with the error message in its tooltip.
Credentials
Section titled “Credentials”- API Access Token — obtain from the SEEK developer portal under your hirer account
- Advertiser ID — your SEEK advertiser/hirer ID (e.g.
seekAnzPublicTest:advertiser:12345)
Credentials are encrypted at rest using AES-GCM before being stored in the database.