Skip to main content

Talent Activity

The talent activity system provides an audit trail of all talent lifecycle events. Activities are logged automatically via domain events and can also be created manually (e.g., recruiter notes).

Architecture

Domain services emit events via @nestjs/event-emitter. The TalentActivityListener captures these events and persists TalentActivity records. A DataLoader prevents N+1 queries when resolving activities on talent objects.

Cross-service events: Skill activities (SKILL_ADDED) are written directly to the TalentActivity table by background-tasks (Python/AsyncPG) since skills are extracted during CV parsing pipelines. The backend listener handles all other 20 event types.

Activity Types

TypeTrigger
TALENT_CREATEDNew talent profile created
TALENT_UPDATEDProfile fields modified
TALENT_DELETEDTalent removed
TALENT_VIEWEDProfile viewed by a user
CV_UPLOADEDCV file uploaded
CV_UPDATEDCV replaced or edited
CV_DELETEDCV removed
CV_PARSEDAI parsing completed
ADDED_TO_POOLTalent added to a pool
REMOVED_FROM_POOLTalent removed from a pool
POOL_CHANGEDTalent moved between pools
TALENT_SHAREDShared with user or company
TALENT_SHARE_REVOKEDShare access revoked
CANDIDATE_CREATEDAdded as job candidate
CANDIDATE_STATUS_CHANGEDCandidate status updated
CANDIDATE_STAGE_CHANGEDMoved to different pipeline stage
NOTE_ADDEDRecruiter note added
SKILL_ADDEDSkill added to talent (emitted by background-tasks)
SKILL_REMOVEDSkill removed from talent (not yet implemented)
EXTERNAL_SHARE_CREATEDExternal share link generated
EXTERNAL_SHARE_ACCESSEDExternal share link viewed
HIGH_MATCH_DETECTEDHigh job match score detected

GraphQL Queries

List Activities (Company-Scoped)

query {
talentActivities(
filter: {
activityTypes: [CV_UPLOADED, NOTE_ADDED]
talentId: "talent-uuid"
performedById: "user-uuid"
}
pagination: { skip: 0, take: 20 }
) {
activities {
id
activityType
description
metadata
createdAt
talent { id firstName lastName }
performedBy { id firstName lastName }
}
totalCount
hasMore
}
}

Talent Activity History

query {
talentActivityHistory(
talentId: "talent-uuid"
pagination: { skip: 0, take: 50 }
) {
activities { id activityType description createdAt }
totalCount
hasMore
}
}

Activities on Talent Object

query {
talent(id: "talent-uuid") {
id
firstName
activities(limit: 10) {
id activityType description createdAt
}
}
}

Manual Activity Creation

Recruiters can create manual activity entries (e.g., interview notes, status updates):

mutation {
createTalentActivity(input: {
talentId: "talent-uuid"
activityType: NOTE_ADDED
description: "Interview completed - strong technical skills"
metadata: { rating: 4, stage: "technical" }
}) { id activityType description createdAt }
}

mutation { deleteTalentActivity(id: "activity-uuid") }

Data Model

Each activity stores talentId, companyId, activityType, performedById, description, and optional metadata (JSON). Client context fields ipAddress and userAgent are captured when available.

Indexes: talentId, companyId, activityType, performedById, createdAt DESC, composites on (companyId, talentId), (companyId, activityType), (talentId, createdAt DESC).

info

Activities cascade-delete when the parent talent or company is removed. The performedById field is set to null if the performing user is deleted.