HireEQ: AI Hiring Platform
An AI video-screening platform for hiring. Recorded interview answers are pulled from S3, transcoded and frame-sampled with FFmpeg, transcribed with Whisper, scored against a facial-analysis service, and turned into a recruiter report and an Excel export. I built the NestJS backend and the whole asynchronous pipeline.




Overview
A candidate answers interview questions on video. The platform picks the recordings up, works through them out of band, and gives the hiring team a report: a transcript aligned to the video, per-second signals they can scrub through, and a spreadsheet they can forward. The interesting engineering is not the scoring — it is that a job which takes minutes of CPU per candidate has to sit behind an API that answers in milliseconds.
The pipeline
A NestJS monorepo split into an API app and a worker app over two durable RabbitMQ queues, with a shared library holding the queue wrapper, the S3 client, the response interceptor and the schemas. Submitting an interview emits one event and returns; the worker then chains the stages itself — download, extract frames, process frames, merge, report. Messages are persistent, prefetch is one, and acknowledgement is manual and only after the work resolves, so a crashed worker redelivers rather than losing the job.
Making replays safe
Redelivery only helps if the work is safe to repeat, so idempotency is built into the data layer rather than bolted on: every write is an upsert keyed on the job and candidate pair, transcription checks for an existing transcript before spending money on Whisper again, the report short-circuits on a cached result unless explicitly forced, and the language-model stage is guarded by a processed flag. Re-running a stage is cheap and harmless; that is what makes the manual re-trigger endpoints a usable repair tool rather than a footgun.
The media path
FFmpeg runs only in the worker and does three distinct jobs: sampling frames at one per second for the facial-analysis pass, extracting an audio track for transcription — emitted from the same command as the frames rather than a second decode — and concatenating a candidate's per-question answers into a single video with a concat list rather than a re-encode per file. Whisper is asked for verbose JSON so the transcript carries segment timestamps, which is what lets the player highlight the sentence being spoken.
Boundaries and hardening
Twenty-two environment variables are validated with Joi at boot, so the service refuses to start misconfigured instead of failing on the first request that needs a missing key. Authentication is a global guard with an explicit public opt-out, and tokens are checked against the database so they can actually be revoked — a deliberate trade of one read per request for a real revocation story. Validation runs globally with unknown properties stripped, responses go through one transform interceptor, and the API is documented with Swagger behind bearer auth.
Honest scope
I built the backend and the pipeline. The emotion analysis itself is a third-party facial-analysis service and a separate prediction service, not something I trained; the language-model work is Whisper for transcription and a chat model for aligning a transcript against a résumé. The web dashboard in this codebase never got past a visualisation prototype — the charts and the timestamped video player are real, the data behind them was not wired up — and the mobile app is a recruiter client, not the candidate recorder. There are no automated tests, which is the thing I would fix first.