Pipeline System
Pipelines are Kanban-style boards for managing recruitment workflows. Each pipeline belongs to a company and an owner, contains jobs via PipelineItem records, and tracks candidates through status stages.
Pipeline Structure
Core Enums
| Enum | Values |
|---|---|
PipelineType | ACTIVE, PRIVATE, ARCHIVED |
ShareableType | SHARABLE, NOT_SHARABLE |
PipelineItemStatus | NEW, IN_PROGRESS, IN_FOCUS, PENDING_FEEDBACK, CLOSED, FEATURE_REQUEST |
CandidateStatus | NEW, IN_REVIEW, INTERVIEW, APPROVED, REJECTED, WITHDRAWN |
Candidate Status Flow
Candidates move through these stages on the pipeline board:
Withdrawals can happen from any active stage. When a candidate withdraws, the status changes to WITHDRAWN and a withdrawal reason is persisted.
Pipeline Item Status Transitions
Pipeline items (jobs within a pipeline) follow their own status lifecycle. Some transitions require a reason field:
| From | Allowed Targets | Reason Required? |
|---|---|---|
NEW | IN_PROGRESS, IN_FOCUS, PENDING_FEEDBACK, CLOSED, FEATURE_REQUEST | PENDING_FEEDBACK and CLOSED require reason |
IN_PROGRESS | NEW, IN_FOCUS, PENDING_FEEDBACK, CLOSED | PENDING_FEEDBACK and CLOSED require reason |
IN_FOCUS | IN_PROGRESS, PENDING_FEEDBACK, CLOSED | PENDING_FEEDBACK and CLOSED require reason |
PENDING_FEEDBACK | IN_PROGRESS, IN_FOCUS, CLOSED | CLOSED requires reason |
CLOSED | NEW, IN_PROGRESS, FEATURE_REQUEST | All require reason |
FEATURE_REQUEST | NEW, IN_PROGRESS, CLOSED | CLOSED requires reason |
Scoring Configuration
Each pipeline can have a PipelineScoringConfig that controls how AI match scores are weighted:
| Weight | Default | Purpose |
|---|---|---|
skillWeight | 0.40 | Skill-based matching |
semanticWeight | 0.35 | Semantic similarity |
contextWeight | 0.25 | Contextual relevance |
The innerWeights JSON field allows fine-grained sub-weight tuning.
Key GraphQL Operations
Pipeline CRUD
# Create pipeline with scoring config
mutation {
createPipeline(input: {
name: "Q1 Engineering Hires"
description: "Backend and frontend roles"
companyId: "company-uuid"
pipelineType: ACTIVE
shareableType: SHARABLE
scoringConfig: {
skillWeight: 0.45
semanticWeight: 0.30
contextWeight: 0.25
}
}) { id name pipelineType }
}
# List pipelines for current user
query {
pipelines {
id name pipelineType
owner { firstName lastName }
pipelineItems { id job { title } status }
}
}
Pipeline Items (Jobs)
# Add job to pipeline
mutation {
createPipelineItem(input: {
pipelineId: "pipeline-uuid"
jobId: "job-uuid"
}) { id status }
}
# Update pipeline item status
mutation {
updatePipelineItem(input: {
id: "item-uuid"
status: IN_PROGRESS
}) { id status }
}
Candidate Management
# Add talent as candidate to a job
mutation {
addTalentAsCandidate(addTalentAsCandidateInput: {
talentId: "talent-uuid"
jobId: "job-uuid"
cvId: "cv-uuid"
}) { id status }
}
# Self-apply as candidate
mutation {
applyAsCandidate(applyAsCandidateInput: {
jobId: "job-uuid"
motivation: "Excited about this role"
}) { id status }
}
# Update candidate status
mutation {
updateCandidate(updateCandidateInput: {
id: "candidate-uuid"
status: INTERVIEW
}) { id status }
}
applyAsCandidate prevents duplicate applications per user/job. It auto-provisions a talent pool entry and generates default motivation text when not supplied.
Pipeline Sharing
Pipelines support four sharing mechanisms:
| Type | Model | Permission Levels |
|---|---|---|
| User share | PipelineUserShare | VIEW, EDIT, MANAGE |
| Company share | PipelineCompanyShare | VIEW, EDIT, MANAGE |
| External share | PipelineExternalShare | VIEW only (token-based) |
| Collaborator share | CollaboratorPipelineShare | VIEW, EDIT, MANAGE |
Access Control Matrix
| Role | Create | Read | Update | Delete | Share | Manage Items |
|---|---|---|---|---|---|---|
| Pipeline Owner | Y | Y | Y | Y | Y | Y |
| Company Admin | Y | Y | Y | Y | Y | Y |
| Company Member | Y | Y | - | - | - | Y |
| Shared (MANAGE) | - | Y | Y | - | Y | Y |
| Shared (EDIT) | - | Y | Y | - | - | Y |
| Shared (VIEW) | - | Y | - | - | - | - |
| External Token | - | Y | - | - | - | - |
# Share pipeline with user
mutation {
createPipelineUserShare(input: {
pipelineId: "pipeline-uuid"
userId: "user-uuid"
permissionLevel: EDIT
}) { id permissionLevel }
}
# External share (token-based, with expiry)
mutation {
createPipelineExternalShare(input: {
pipelineId: "pipeline-uuid"
email: "partner@example.com"
expirationHours: 168
}) { id token expiresAt }
}
Authorization
The PipelineAuthorizationService provides centralized access checks:
canAccessPipeline(userId, pipelineId)-- read access (returns boolean)hasWriteAccess(userId, pipelineId)-- write access (returns boolean)getAccessiblePipelineIds(userId)-- all pipelines the user can see
Access is resolved by checking, in order: ownership, company admin/member status, user/company shares, and collaborator relationships (only ACTIVE collaborators).