Recruitment API
All authenticated endpoints require a valid Cloudflare Access JWT and appropriate permissions.
Base paths:
- Authenticated:
/api/recruitment - Public (no auth):
/api/careers
Public Careers Endpoints
Section titled “Public Careers Endpoints”GET /api/careers/jobs
Section titled “GET /api/careers/jobs”Returns all published jobs for the public careers page.
Response
{ "jobs": [ { "id": "string", "title": "string", "department": "string | null", "location": "string | null", "workplace_type": "hybrid", "employment_type": "full_time", "experience_level": "string | null", "published_at": "string | null" } ]}GET /api/careers/jobs/:id
Section titled “GET /api/careers/jobs/:id”Returns a single published job with full details (description, requirements, benefits).
POST /api/careers/apply
Section titled “POST /api/careers/apply”Submit a public application. Upserts the candidate (by email) and creates an application.
Request
{ "job_id": "string (required)", "first_name": "string (required)", "last_name": "string (required)", "email": "string (required)", "phone": "string", "location": "string", "linkedin_url": "string", "cover_letter": "string", "source_channel": "string"}Response 200
{ "success": true, "application_id": "string" }Error 409 — duplicate application for same job+candidate.
GET /api/recruitment/jobs
Section titled “GET /api/recruitment/jobs”List jobs. Filterable by status.
Query params: status (draft, published, paused, closed, filled, or omit for all)
Permission: recruitment:view
Response includes stage_counts array with candidate counts per pipeline stage.
GET /api/recruitment/jobs/:id
Section titled “GET /api/recruitment/jobs/:id”Get job detail with pipeline stages (including candidate counts) and hiring team.
Response
{ "job": { "..." }, "stages": [ { "id": "string", "name": "string", "position": 0, "candidate_count": 5 } ], "team": [ { "user_id": "string", "role": "recruiter", "name": "string", "email": "string" } ]}POST /api/recruitment/jobs
Section titled “POST /api/recruitment/jobs”Create a new job. Defaults to draft status with the default pipeline template.
Permission: recruitment:manage
PUT /api/recruitment/jobs/:id
Section titled “PUT /api/recruitment/jobs/:id”Update job fields (title, description, location, etc.).
Permission: recruitment:manage
PUT /api/recruitment/jobs/:id/status
Section titled “PUT /api/recruitment/jobs/:id/status”Transition job status. Valid transitions:
draft→publishedpublished→paused,closedpaused→published,closedclosed→draft
Request: { "status": "published" }
Job Candidates
Section titled “Job Candidates”GET /api/recruitment/jobs/:id/candidates
Section titled “GET /api/recruitment/jobs/:id/candidates”List applications for a job with joined candidate info and stage details.
Query params: stage_id, status (default: active)
GET /api/recruitment/jobs/:id/stats
Section titled “GET /api/recruitment/jobs/:id/stats”Aggregate stats: total, active, rejected, hired, new_this_week.
Hiring Team
Section titled “Hiring Team”GET /api/recruitment/jobs/:id/team
Section titled “GET /api/recruitment/jobs/:id/team”List hiring team members for a job.
POST /api/recruitment/jobs/:id/team
Section titled “POST /api/recruitment/jobs/:id/team”Add a team member. Request: { "user_id": "string", "role": "recruiter" }
Roles: recruiter, hiring_manager, interviewer, coordinator
DELETE /api/recruitment/jobs/:id/team/:userId
Section titled “DELETE /api/recruitment/jobs/:id/team/:userId”Remove a team member.
Job Requirements
Section titled “Job Requirements”GET /api/recruitment/jobs/:id/requirements
Section titled “GET /api/recruitment/jobs/:id/requirements”List requirements for a job (used in AI scoring).
PUT /api/recruitment/jobs/:id/requirements
Section titled “PUT /api/recruitment/jobs/:id/requirements”Bulk replace requirements. Request: { "requirements": [...] }
Candidates
Section titled “Candidates”GET /api/recruitment/candidates
Section titled “GET /api/recruitment/candidates”Search candidates with pagination.
Query params: search, source, job_id, stage_id, page
GET /api/recruitment/candidates/:id
Section titled “GET /api/recruitment/candidates/:id”Get candidate with their applications and activity history.
POST /api/recruitment/candidates
Section titled “POST /api/recruitment/candidates”Create a candidate. Returns { candidate, existing: boolean } — if the email exists, returns the existing record.
PUT /api/recruitment/candidates/:id
Section titled “PUT /api/recruitment/candidates/:id”Update candidate fields.
Applications
Section titled “Applications”POST /api/recruitment/applications
Section titled “POST /api/recruitment/applications”Create an application (candidate × job). Assigns to the entry stage of the job’s pipeline.
Request: { "job_id": "string", "candidate_id": "string", "source_channel": "string" }
PUT /api/recruitment/applications/:id/stage
Section titled “PUT /api/recruitment/applications/:id/stage”Move an application to a new pipeline stage. Creates an audit trail entry.
Request: { "stage_id": "string", "note": "string" }
PUT /api/recruitment/applications/:id/reject
Section titled “PUT /api/recruitment/applications/:id/reject”Reject an application. Records the rejection stage and reason.
Request: { "rejection_reason": "string" }
PUT /api/recruitment/applications/:id/withdraw
Section titled “PUT /api/recruitment/applications/:id/withdraw”Withdraw an application (candidate-initiated).
PUT /api/recruitment/applications/:id/hire
Section titled “PUT /api/recruitment/applications/:id/hire”Hire the candidate. Creates a user account, links candidate record, assigns onboarding template, and logs the hire activity.
Request:
{ "start_date": "2025-04-01", "job_title": "Senior Engineer", "access_level": "employee", "onboarding_template_id": "tpl-onb-general"}Response:
{ "success": true, "user_id": "string", "existing_user": false}Timeline & Notes
Section titled “Timeline & Notes”GET /api/recruitment/applications/:id/timeline
Section titled “GET /api/recruitment/applications/:id/timeline”Get activity timeline for an application.
POST /api/recruitment/applications/:id/notes
Section titled “POST /api/recruitment/applications/:id/notes”Add a note to an application. Request: { "note": "string" }
Activities
Section titled “Activities”GET /api/recruitment/activities
Section titled “GET /api/recruitment/activities”Global activity stream. Query params: job_id, action, page
Pipeline Templates
Section titled “Pipeline Templates”GET /api/recruitment/pipeline-templates
Section titled “GET /api/recruitment/pipeline-templates”List all pipeline templates with their stages.
GET /api/recruitment/pipeline-templates/:id
Section titled “GET /api/recruitment/pipeline-templates/:id”Get a single template with stages.
Email Templates
Section titled “Email Templates”GET /api/recruitment/email-templates
Section titled “GET /api/recruitment/email-templates”List all email templates.
Permission: recruitment:view
Response
{ "templates": [ { "id": "string", "name": "string", "subject": "string", "body": "string (HTML)", "category": "general | thank_you | interview | rejection | offer", "is_active": 1, "created_by": "string", "created_at": "string", "updated_at": "string" } ]}GET /api/recruitment/email-templates/:id
Section titled “GET /api/recruitment/email-templates/:id”Get a single email template.
POST /api/recruitment/email-templates
Section titled “POST /api/recruitment/email-templates”Create an email template.
Permission: recruitment:manage
Request
{ "name": "string (required)", "subject": "string (required)", "body": "string (required, HTML with {{merge.fields}})", "category": "general (default)"}PUT /api/recruitment/email-templates/:id
Section titled “PUT /api/recruitment/email-templates/:id”Update an email template.
Permission: recruitment:manage
DELETE /api/recruitment/email-templates/:id
Section titled “DELETE /api/recruitment/email-templates/:id”Delete an email template.
Permission: recruitment:manage
POST /api/recruitment/email-templates/:id/preview
Section titled “POST /api/recruitment/email-templates/:id/preview”Preview a template with merge fields resolved using a sample candidate.
Permission: recruitment:view
Request
{ "candidate_id": "string (required)", "application_id": "string (optional)"}Response
{ "subject": "string (resolved)", "body": "string (HTML, resolved)"}Communication
Section titled “Communication”POST /api/recruitment/candidates/:id/send-email
Section titled “POST /api/recruitment/candidates/:id/send-email”Send an email to a candidate via Gmail API. Records in communication log and activity feed.
Permission: recruitment:update
Request
{ "subject": "string (required)", "body": "string (required, HTML)", "application_id": "string (optional)", "template_id": "string (optional)"}Response
{ "communication_id": "string", "gmail_message_id": "string"}GET /api/recruitment/candidates/:id/communications
Section titled “GET /api/recruitment/candidates/:id/communications”Get communication log for a candidate.
Permission: recruitment:view
Response
{ "communications": [ { "id": "string", "subject": "string", "body": "string", "to_email": "string", "from_email": "string", "status": "sent | failed", "sent_at": "string", "sent_by_name": "string | null", "template_name": "string | null" } ]}POST /api/recruitment/bulk-email
Section titled “POST /api/recruitment/bulk-email”Send personalised emails to multiple candidates. Merge fields are resolved per candidate.
Permission: recruitment:manage
Request
{ "candidate_ids": ["string"], "subject": "string (required)", "body": "string (required, HTML with {{merge.fields}})", "template_id": "string (optional)"}Stage Email Rules
Section titled “Stage Email Rules”GET /api/recruitment/pipeline-templates/:id/email-rules
Section titled “GET /api/recruitment/pipeline-templates/:id/email-rules”Get auto-send email rules for a pipeline template.
Permission: recruitment:view
Response
{ "rules": [ { "id": "string", "stage_id": "string", "template_id": "string", "trigger_on": "enter", "is_active": 1 } ]}PUT /api/recruitment/pipeline-templates/:id/email-rules
Section titled “PUT /api/recruitment/pipeline-templates/:id/email-rules”Replace all email rules for a pipeline template.
Permission: recruitment:manage
Request
{ "rules": [ { "stage_id": "string", "template_id": "string", "is_active": true } ]}Interviews
Section titled “Interviews”GET /api/recruitment/interviews
Section titled “GET /api/recruitment/interviews”List interviews with optional status filter.
Permission: recruitment:view
Query params: status (scheduled, completed, cancelled), application_id, candidate_id
Response includes interview details with joined candidate name, job title, and participants.
GET /api/recruitment/interviews/:id
Section titled “GET /api/recruitment/interviews/:id”Get interview details with participants.
Permission: recruitment:view
POST /api/recruitment/interviews
Section titled “POST /api/recruitment/interviews”Schedule an interview. Optionally creates a Google Calendar event with attendees.
Permission: recruitment:update
Request
{ "application_id": "string (required)", "title": "string (required)", "interview_type": "video_call | phone | in_person | take_home", "scheduled_at": "string (ISO datetime, required for manual)", "duration_minutes": 60, "location": "string", "description": "string", "scheduling_type": "manual | self_schedule", "self_schedule_slots": [{ "start": "string", "end": "string" }], "participant_ids": ["string (user IDs)"], "create_calendar_event": true}PUT /api/recruitment/interviews/:id
Section titled “PUT /api/recruitment/interviews/:id”Update interview details.
Permission: recruitment:update
PUT /api/recruitment/interviews/:id/cancel
Section titled “PUT /api/recruitment/interviews/:id/cancel”Cancel an interview. Deletes the Google Calendar event if one exists.
Permission: recruitment:update
Request: { "reason": "string" }
PUT /api/recruitment/interviews/:id/complete
Section titled “PUT /api/recruitment/interviews/:id/complete”Mark an interview as completed.
Permission: recruitment:update
PUT /api/recruitment/interviews/:id/participants/:userId/feedback
Section titled “PUT /api/recruitment/interviews/:id/participants/:userId/feedback”Submit feedback and rating for a participant.
Permission: recruitment:update
Request
{ "feedback": "string", "rating": 4}Self-Scheduling (Public — No Auth)
Section titled “Self-Scheduling (Public — No Auth)”GET /api/careers/schedule/:token
Section titled “GET /api/careers/schedule/:token”Get self-scheduling interview details and available time slots for a candidate.
Response
{ "interview": { "id": "string", "title": "string", "interview_type": "string", "duration_minutes": 60, "description": "string | null" }, "candidate_name": "string", "slots": [{ "start": "string", "end": "string" }]}POST /api/careers/schedule/:token
Section titled “POST /api/careers/schedule/:token”Confirm a selected time slot. Creates a Google Calendar event and updates the interview.
Request: { "start": "string (ISO datetime)" }
Comments
Section titled “Comments”GET /api/recruitment/candidates/:id/comments
Section titled “GET /api/recruitment/candidates/:id/comments”List comments for a candidate, including author name. Supports threading via parent_id.
Permission: recruitment:view
POST /api/recruitment/candidates/:id/comments
Section titled “POST /api/recruitment/candidates/:id/comments”Add a comment to a candidate profile.
Permission: recruitment:update
Request
{ "content": "string (required)", "application_id": "string (optional)", "mentions": ["string (user IDs)"], "parent_id": "string (for replies)"}PUT /api/recruitment/comments/:id
Section titled “PUT /api/recruitment/comments/:id”Update a comment. Only the original author can edit.
Permission: recruitment:update
Request: { "content": "string" }
DELETE /api/recruitment/comments/:id
Section titled “DELETE /api/recruitment/comments/:id”Delete a comment.
Permission: recruitment:manage
Scorecard Templates
Section titled “Scorecard Templates”GET /api/recruitment/scorecard-templates
Section titled “GET /api/recruitment/scorecard-templates”List all scorecard templates with linked stage/job names and criteria count.
Permission: recruitment:view
Response
{ "templates": [ { "id": "string", "name": "string", "description": "string | null", "pipeline_stage_id": "string | null", "job_id": "string | null", "is_default": 0, "stage_name": "string | null", "job_title": "string | null", "criteria_count": 5, "created_at": "string", "updated_at": "string" } ]}GET /api/recruitment/scorecard-templates/:id
Section titled “GET /api/recruitment/scorecard-templates/:id”Get a scorecard template with all its criteria.
Permission: recruitment:view
Response
{ "template": { "..." }, "criteria": [ { "id": "string", "scorecard_template_id": "string", "name": "string", "description": "string | null", "category": "technical | communication | culture | leadership", "weight": 1, "sort_order": 0, "anchors": "{\"1\": \"Poor\", \"3\": \"Meets expectations\", \"5\": \"Exceptional\"}" } ]}POST /api/recruitment/scorecard-templates
Section titled “POST /api/recruitment/scorecard-templates”Create a new scorecard template with criteria.
Permission: recruitment:manage
Request
{ "name": "string (required)", "description": "string", "pipeline_stage_id": "string", "job_id": "string", "is_default": false, "criteria": [ { "name": "string (required)", "description": "string", "category": "technical", "weight": 1, "sort_order": 0, "anchors": { "1": "Poor", "5": "Exceptional" } } ]}PUT /api/recruitment/scorecard-templates/:id
Section titled “PUT /api/recruitment/scorecard-templates/:id”Update a scorecard template. If criteria is provided, existing criteria are replaced entirely.
Permission: recruitment:manage
DELETE /api/recruitment/scorecard-templates/:id
Section titled “DELETE /api/recruitment/scorecard-templates/:id”Delete a scorecard template and all its criteria.
Permission: recruitment:manage
Evaluations
Section titled “Evaluations”GET /api/recruitment/applications/:id/evaluations
Section titled “GET /api/recruitment/applications/:id/evaluations”List evaluations for an application. Implements independent scoring: if the current user has not submitted their own evaluation, other evaluators’ scores and recommendations are hidden (only names and submission status shown).
Permission: recruitment:view
Response
{ "evaluations": [ { "id": "string", "application_id": "string", "evaluator_id": "string", "evaluator_name": "string", "evaluator_picture": "string | null", "scorecard_template_id": "string | null", "interview_id": "string | null", "recommendation": "strong_yes | yes | no_decision | no | strong_no | null (hidden)", "overall_score": "number | null (hidden)", "is_submitted": 0, "submitted_at": "string | null" } ], "can_see_scores": true, "my_evaluation_id": "string | null"}GET /api/recruitment/evaluations/:id
Section titled “GET /api/recruitment/evaluations/:id”Get a single evaluation with all criterion scores. Requires the requesting user to have submitted their own evaluation first (or have manage permission).
Permission: recruitment:view
POST /api/recruitment/applications/:id/evaluations
Section titled “POST /api/recruitment/applications/:id/evaluations”Create a new evaluation draft. One evaluation per user per application per interview.
Permission: recruitment:update
Request
{ "scorecard_template_id": "string", "interview_id": "string (optional)", "recommendation": "strong_yes | yes | no_decision | no | strong_no", "notes": "string", "scores": [ { "criteria_id": "string", "score": 4, "evidence": "Demonstrated strong problem-solving skills" } ]}PUT /api/recruitment/evaluations/:id
Section titled “PUT /api/recruitment/evaluations/:id”Update an evaluation. Only the evaluator (owner) can edit. Submitted evaluations cannot be modified.
Permission: recruitment:update
PUT /api/recruitment/evaluations/:id/submit
Section titled “PUT /api/recruitment/evaluations/:id/submit”Submit an evaluation. Validates all criteria have scores, computes weighted average overall_score, sets is_submitted=1 and submitted_at. Cannot be undone.
Permission: recruitment:update
Response: { "success": true, "overall_score": 3.86 }
DELETE /api/recruitment/evaluations/:id
Section titled “DELETE /api/recruitment/evaluations/:id”Delete a draft evaluation. Only the evaluator can delete, and only if not yet submitted.
Permission: recruitment:update
GET /api/recruitment/applications/:id/debrief
Section titled “GET /api/recruitment/applications/:id/debrief”Aggregate debrief summary for all submitted evaluations. Only accessible after submitting your own evaluation (or with manage permission).
Permission: recruitment:view
Response
{ "evaluations": ["...all submitted evaluations with scores"], "criteria_averages": [ { "criteria_id": "string", "criteria_name": "Technical Skills", "criteria_category": "technical", "average": 3.67, "std_dev": 0.47, "count": 3, "min": 3, "max": 4 } ], "recommendation_distribution": { "strong_yes": 1, "yes": 2 }, "overall_average": 3.86, "total_evaluations": 3}AI Scoring & Talent Pool
Section titled “AI Scoring & Talent Pool”POST /api/recruitment/applications/:id/score
Section titled “POST /api/recruitment/applications/:id/score”Score a single application using AI + rule-based matching.
Permission: recruitment:update
Response
{ "success": true, "score": 72, "summary": "Strong match for technical requirements..."}POST /api/recruitment/jobs/:id/score-all
Section titled “POST /api/recruitment/jobs/:id/score-all”Batch score all active applications for a job.
Permission: recruitment:manage
Response
{ "success": true, "scored": 12, "failed": 0, "results": [ { "applicationId": "string", "success": true, "score": 85 } ]}POST /api/recruitment/candidates/:id/extract-resume
Section titled “POST /api/recruitment/candidates/:id/extract-resume”Extract text from a candidate’s uploaded resume PDF.
Permission: recruitment:update
Response
{ "success": true, "resume_text": "Extracted text content...", "length": 4523}PUT /api/recruitment/candidates/:id/talent-pool
Section titled “PUT /api/recruitment/candidates/:id/talent-pool”Toggle a candidate’s talent pool status.
Permission: recruitment:update
Request
{ "talent_pool": true}Response
{ "success": true, "talent_pool": true}GET /api/recruitment/candidates/duplicates
Section titled “GET /api/recruitment/candidates/duplicates”Find potential duplicate candidates by email or name.
Permission: recruitment:view
Query params: email, name
Response
{ "candidates": [ { "id": "string", "first_name": "string", "last_name": "string", "email": "string", "phone": "string | null", "source": "string", "created_at": "string" } ]}POST /api/recruitment/jobs/:id/requirements/extract
Section titled “POST /api/recruitment/jobs/:id/requirements/extract”Use AI to extract structured requirements from a job description.
Permission: recruitment:update
Response
{ "success": true, "requirements": [ { "category": "required", "description": "5+ years experience in React and TypeScript", "priority": 4, "weight": 1.0, "position": 1 } ]}LinkedIn Enrichment
Section titled “LinkedIn Enrichment”POST /api/recruitment/candidates/:id/enrich
Section titled “POST /api/recruitment/candidates/:id/enrich”Enrich a candidate’s profile from LinkedIn via Scrapin.io. Requires linkedin_url on the candidate record and a configured Scrapin connection.
Permission: recruitment:update
Response
{ "success": true, "profile": { "fullName": "Jane Doe", "headline": "Senior Software Engineer", "currentCompany": "Acme Corp", "currentTitle": "Senior Software Engineer", "location": "Melbourne, Australia", "experiences": [], "education": [], "skills": ["TypeScript", "React", "Node.js"], "certifications": [], "languages": ["English"] }}GET /api/recruitment/candidates/:id/linkedin
Section titled “GET /api/recruitment/candidates/:id/linkedin”Get stored LinkedIn enrichment data for a candidate.
Permission: recruitment:view
Response
{ "linkedin_url": "https://linkedin.com/in/janedoe", "linkedin_data": { "...structured profile..." }, "enriched_at": "2026-03-09T10:00:00Z"}