Case study / Automation · Job search
Kairo
From scattered sources to opportunities with context.
A platform for discovering, monitoring and managing job opportunities with explainable matching and supervised applications.
- Technologies used
- Next.js · React · NestJS · TypeScript · PostgreSQL · Prisma · Redis · BullMQ · OpenAI · Playwright
The problem
Job search requires revisiting sources, removing duplicates, detecting changes and preparing documents. Kairo turns that work into processes with states and validation while keeping application decisions under user control.
My contribution
I build the Next.js frontend, NestJS API and an independent BullMQ worker. I implement ingestion, ATS adapters, matching, AI budgets and the opportunity/application domain with private, versioned materials.
What the product enables
Discover and maintain a source universe
Discovery records candidates and evidence before ingestion. Monitoring queries known sources such as Lever, Greenhouse and Ashby through adapters; it normalizes jobs, detects changes and retains runs and errors.
Prioritize with an explanation
Profile and preferences drive validation and deterministic matching. Unknown attributes are distinguished from incompatibilities. Users manage opportunity stages without a score automatically changing their decision.
Prepare and authorize an application
Resumes, cover letters and answers are prepared from explicit sources with human review. Apply authorizes one application and one specific package. Runners for compatible forms request intervention for CAPTCHA, questions or unsupported controls.
How the pieces fit together
A workspace monorepo separates Web, API and Worker. PostgreSQL/Prisma stores sources, jobs, profiles, opportunities and packages. Redis/BullMQ runs discovery, monitoring, validation and matching through jobs and schedulers. OpenAI handles bounded tasks; normalization, validation and deterministic scoring remain outside the model.
- Next.js WebProfile · opportunities · authorizationsNestJS API
- API → Redis / BullMQDiscovery · Monitoring · validationWorker → ATS
- WorkerNormalization · matching · historyPostgreSQL / Prisma
- Reviewed package + ApplyVersion · checksum · authorizationSupervised runner
Engineering decisions
Separate external evidence from user state
Discovery stages candidates before canonical ingestion. Monitoring normalizes and deduplicates through adapters and only closes jobs after a complete fetch. Validation and matching are deterministic; neither a score nor a source failure changes the opportunity stage chosen by the user.
AI has a budget and boundaries
Calls record consumption in AiUsage and pass through reservations and budget controls. Structured Outputs and validation distinguish a provider response from a fact accepted by the domain.
Authorize immutable materials
ApplicationPackage freezes profile, job, answers and documents. A material change invalidates authorization. The runner uses the reviewed package without generating new AI text during submission.
Challenges addressed
Avoid duplicate and uncertain submissions
submitIntentAt is recorded before the single final submission. An ambiguous response leaves SUBMISSION_UNKNOWN and is not retried automatically. Form compatibility is inspected and expires; recognizing the ATS is not enough.
Prepare documents without inventing experience
Materials retain sources and hashes. Semantic generation is validated and reviewed before authorization; local extractive preparation provides an alternative without AI calls.
Demonstrable capabilities
Automation with domain state
Persisted runs trace source failures and job changes through to prioritization.
Assistance under human control
A reviewed package and per-application authorization define what the runner may submit. Compatibility remains limited to supported forms.