MF.
ESEN
Back to the portfolio

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.

MVP component relationships
  1. Next.js Web
    Profile · opportunities · authorizations
    NestJS API
  2. API → Redis / BullMQ
    Discovery · Monitoring · validation
    Worker → ATS
  3. Worker
    Normalization · matching · history
    PostgreSQL / Prisma
  4. Reviewed package + Apply
    Version · checksum · authorization
    Supervised 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.