Jobs & Candidates
The hiring system manages open roles (jobs), applicants (candidates), job-candidate assignments, placements, and referrals. Used by Paula via the admin portal's Hiring section.
Jobs
GET /api/jobs
List jobs (admin use — all fields).
Auth: Required
Query params:
| Param | Type | Description |
|---|---|---|
status | string | open, closed, on_hold |
product | string | Filter by product |
limit | number | Default: 50 |
GET /api/jobs/public
List open jobs for public display (limited fields: id, title, product, location, job_type, description, requirements, created_at).
Auth: Public
Response: Filtered to status = 'open' only.
GET /api/jobs/public/:id
Get a single open job by ID (public, limited fields).
Auth: Public
GET /api/jobs/:id
Get a full job with all candidates.
Auth: Required
Response: All job fields + candidates array.
POST /api/jobs
Create a job.
Auth: Required
| Field | Type | Required | Description |
|---|---|---|---|
title | string | yes | Job title |
product | string | no | Default: studios |
team | string | no | Team name |
description | string | no | Full job description |
requirements | string | no | Requirements |
location | string | no | Default: Remote |
job_type | string | no | contract, full_time, part_time. Default: contract |
status | string | no | Default: open |
salary_range | string | no | e.g. $80k-$120k |
PATCH /api/jobs/:id
Update: title, product, team, description, requirements, location, job_type, status, salary_range.
GET /api/jobs/:id/activities
Get all activities for a job.
Auth: Required
GET /api/jobs/:id/notes
Get notes for a job.
Auth: Required
POST /api/jobs/:id/notes
Add a note to a job.
Auth: Required
Body: { "body": "Note text", "author": "user@email.com" }
DELETE /api/jobs/:id/notes/:noteId
Delete a job note.
Auth: Required
GET /api/jobs/:id/files
List files attached to a job.
Auth: Required
POST /api/jobs/:id/files
Upload a file to a job (stored in R2).
Auth: Required
Body: Multipart form with file field.
DELETE /api/jobs/:id/files/:fileId
Delete a job file.
Auth: Required
POST /api/jobs/:id/recommend-candidates
Use Claude AI to rank existing candidates by fit for this job.
Auth: Required
Response:
{
"ok": true,
"data": {
"recommendations": [
{
"candidate_id": "cand_a1b2",
"name": "Jane Smith",
"score": 92,
"reasoning": "Strong React + 5 years experience..."
}
]
}
}Candidates
GET /api/candidates
List candidates with rich filtering and pagination.
Auth: Required
Query params:
| Param | Type | Description |
|---|---|---|
job_id | string | Filter by job |
stage | string | Filter by stage (applied, screening, interview, offer, hired, rejected) |
product | string | Filter by product |
source | string | Filter by source (linkedin, referral, direct, manatal) |
location | string | Location substring match |
has_email | 1 | Only candidates with email |
date_from | string | ISO date — created after |
date_to | string | ISO date — created before |
show_archived | 1 | Include archived candidates |
search | string | Search name, email, company, title |
limit | number | Max 200. Default: 50 |
offset | number | Pagination offset |
Response:
{
"ok": true,
"data": [...],
"total": 4228
}GET /api/candidates/:id
Get a candidate with activity log.
Auth: Required
Response: All fields + activities[] + parsed resume_parsed object.
POST /api/candidates
Create a candidate.
Auth: Required
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Full name |
email | string | no | |
phone | string | no | Phone |
linkedin_url | string | no | |
location | string | no | Location |
current_title | string | no | Current job title |
current_company | string | no | Current employer |
resume_text | string | no | Resume full text |
resume_parsed | object | no | Parsed resume JSON |
source | string | no | linkedin, referral, direct. Default: direct |
stage | string | no | Default: applied |
job_id | string | no | Associated job |
notes | string | no | Recruiter notes |
assigned_to | string | no | Recruiter email |
PATCH /api/candidates/:id
Update: name, email, phone, linkedin_url, location, current_title, current_company, stage, notes, assigned_to, skills, availability, desired_roles, languages, github_url, salary_expectation, notice_period, tags, rating, archived_at.
POST /api/candidates/:id/activity
Log an activity for a candidate.
Auth: Required
Body: { "activity_type": "stage_change", "title": "...", "body": "..." }
GET /api/candidates/:id/files
List candidate files (resumes, attachments) from R2.
POST /api/candidates/:id/files
Upload a file for a candidate (R2 storage).
DELETE /api/candidates/:id/files/:fileId
Delete a candidate file.
GET /api/candidates/:id/notes
List candidate notes.
POST /api/candidates/:id/notes
Add a note.
Body: { "body": "...", "author": "..." }
PATCH /api/candidates/:id/notes/:noteId
Edit a note.
DELETE /api/candidates/:id/notes/:noteId
Delete a note.
POST /api/candidates/enrich-batch
Batch enrich candidates via Apollo API. Resolves missing emails, LinkedIn URLs, and company data for up to 50 candidates at a time.
Auth: Required
Body: { "candidate_ids": ["cand_1", "cand_2", ...] }
POST /api/enrich/:contact_id
Enrich a CRM contact with Apollo data (email, phone, LinkedIn, company).
Auth: Required
POST /api/enrich-company/:company_id
Enrich a CRM company with Apollo data (industry, employee count, LinkedIn).
Auth: Required
Candidate Segments
Segments are saved filters for targeting groups of candidates in campaigns.
POST /api/candidate-segments/preview
Preview candidates that would match a segment definition.
Auth: Required
Body: { "filters": { "skills": [...], "location": "...", "stage": "..." } }
GET /api/candidate-segments
List all saved segments.
POST /api/candidate-segments
Create a segment.
Body: { "name": "...", "description": "...", "filters": {...} }
GET /api/candidate-segments/:id
Get a segment.
PATCH /api/candidate-segments/:id
Update a segment.
DELETE /api/candidate-segments/:id
Delete a segment.
