02 / HR & recruiting
From application to first day
Applications, messages and hiring stages in one system. Candidates can check their status in a personal portal.
The task
Applications arrive from different job boards. Recruiters need to avoid duplicates and know who has already spoken to each candidate.
What we built
Candidates, messages and recruitment stages sit together. Each candidate can have an assigned manager and a visible path through to hiring.
Applications from hh.ru and Avito
Duplicate candidate detection
Interview, trial day and hiring stages

More product screens



How it works
- Intake & deduplication
The system accepts webhook events from external platforms and matches a response and a candidate against existing records by source and contact details.
- Assignment & communication
A response is assigned to an owner based on the candidate's identity on that platform; further communication happens through the internal messenger with live updates.
- Hiring stages
Interviews, internships, and the hiring decision are recorded with a status history and reasons for rejection.
- Sync & data lifecycle
A status change is reliably sent back to the platform with retries; personal data is anonymized automatically once the retention period expires.
For your technical team: inside the solution
A connector layer receives events from external platforms and hands them to background jobs that update the candidate and vacancy model; the back office and the candidate portal share the same data with different access rights, and live updates travel over a separate messaging channel.
- Connector layer for external job platforms
- Background intake and sync jobs
- Back office
- Candidate portal
- Live-update channel
Reliability
- Role-based access with a separate, restricted view for security staff.
- Versioned consent for personal-data processing with a date and source.
- Time limits on background jobs to guard against stuck network calls.
- A candidate can request deletion of their own data from the portal after re-confirming their password.
Decisions and trade-offs
An unresolved event is stored in full and can be replayed later instead of being discarded.
An incoming event from an external platform sometimes cannot be processed immediately, for example before a vacancy is synced.
A response is not lost because of a temporary data-sync gap.
Deduplication also checks for a matching candidate by contact details, not only the event identifier.
An external platform can redeliver the same event with a new identifier.
The same candidate is not created twice in the system.
Outbound sync runs through a separate durable mechanism with state and retries.
A status change must be synced to the external platform reliably, not inside a single user HTTP request.
A temporary platform outage does not lose a status update.
Identifying fields are anonymized automatically once the configured retention period expires.
Candidate personal data cannot be kept indefinitely after the process ends.
The personal-data lifecycle is built into the product rather than left to a manual process.
An outgoing message sent by the system is "adopted" once the platform confirms it, instead of creating a duplicate record.
A candidate may message the chat more than once, and an outgoing operator message does not always get a platform identifier right away.
The conversation is not duplicated even when platform confirmation is delayed.
Each outbound sync operation keeps its own state and is processed independently of the others.
Syncing a status change to the platform and sending the candidate an autoreply are different operations, and neither should depend on the outcome of the other.
A failure in one operation does not block or roll back the other.
Technologies
- Python
- Django
- PostgreSQL
- Celery
- Redis
- Channels / WebSocket
- Docker Compose