LiveEngineer · 2025–2026

Talero ATS

An AI hiring platform that scores candidates, explains every score and keeps candidate data in the EU.

Talero ATS is an applicant tracking system that ranks candidates using their CV and their results from Talero's behavioural game. I built all of it: the Node.js API, the Python AI pipeline, the React frontend and the Google Cloud infrastructure. A tester and an HR specialist worked with me on quality and on what hiring teams need.

Software that ranks job applicants counts as high-risk AI under the EU AI Act, so data handling, audit and human review shaped the design from the start. Small and mid-sized companies in Germany have already shown interest in buying it.

300+ CVs processed so far, in four languages.

Visit taleroai.com
taleroai.com
Talero sign-in screen for the candidate assessment

The project

Recruiters have to screen many applicants quickly, and candidates deserve to know why they were ranked the way they were. Talero ATS reads each CV, combines it with the candidate's game results and gives the recruiter a scored recommendation with the evidence behind it. The recruiter makes the decision.

What I built

  • A Node.js and TypeScript API (Express, Zod, BullMQ on Redis) for jobs, applications, scoring queues and live recruiter dashboards over server-sent events.
  • A Python and FastAPI pipeline that reads each CV with one Gemini call on Vertex AI in the EU, at temperature 0 with a fixed JSON schema, so every CV comes back in the same structure.
  • EBHE, a scoring engine that gives skills 30% of the score and splits the other 70% between behavioural traits and work experience according to career stage. Falling short of a requirement costs about three times as much as exceeding it by the same amount.
  • An AI review in four parts (eligibility, skills fit, evidence and uncertainty), combined by a summariser into one recommendation. It runs on a pseudonymised profile that contains no name, contact details or raw CV text.
  • The React recruiter app, and the Docker, CI/CD and monitoring setup that runs everything on Google Cloud.

How a CV becomes a decision

Every step is deliberate about cost, privacy and who is allowed to decide.

  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  1. A CV arrives

    Upload

    A PDF, a DOCX or a scan. The pipeline reads a photographed CV as well as a typed one.

  2. One model reads it

    EU only

    A single Gemini call on Vertex AI in the EU, at temperature 0 with a fixed output shape, so the same CV always comes back the same way.

  3. Rules do the scoring

    No guessing

    Skills count for 30% of the score. The rest splits between behavioural traits and work experience, weighted by career stage.

  4. Four checks, one profile

    Pseudonymised

    Eligibility, skills fit, evidence and uncertainty are judged separately on a profile with no name, contact details or raw CV text.

  5. A recruiter decides

    Human call

    The AI recommends and explains. The decision is a person's, and it is written to an audit log with the model, version and region.

Candidate data stays in the EU

CVs are personal data, so the pipeline has exactly one AI provider: Gemini on Vertex AI in Google's EU region. There is no fallback provider. If Vertex is down, CV analysis stops and the API reports the outage. I chose that over a second provider because a fallback would quietly send personal data to another company in another country. The model also only receives a whitelist of fields, built after identity has been removed.

Replacing a parser that failed silently

The first CV parser used OCR, regular expressions and a skills lexicon, with no AI, to keep the cost per CV close to zero. Then I found a scanned CV that had produced zero characters of text and still received a score of 48, with no warning to anyone. In August 2026 I replaced the parser with a single Gemini call that reads PDFs directly and stops with an error when a CV is empty or unreadable. It costs a little per CV, and a bad upload can no longer turn into a made-up score.

Choosing the model by measurement

Keeping data in the EU limits the choice of models. Gemini 3 is not served in any single EU region, only in Google's EU multi-region, and the region and model have to change together or every call fails. In September 2026 I tested all three Gemini models available there on the real endpoints. The two larger ones took 35 to 94 seconds and returned flat, near-identical scores. Gemini 3.1 Flash-Lite answered in 6 to 10 seconds and was the only one that told candidates apart, so the smallest model went into production.

Built for the EU AI Act

  • A recruiter makes every hiring decision. The AI gives a recommendation with strengths, risks and a confidence level, and its prompts forbid words like "hire" or "reject".
  • The prompts forbid inferring ethnicity, nationality, religion, health or other protected characteristics.
  • Every AI decision is written to an audit log with the model, engine version, processing region and result, and recruiters can download the audit trail for an application as a PDF.
  • A score layer without enough evidence shows "Not enough data" instead of a default number.
ai-pipeline/prompts/summarizer_v1.txt
The compliance rules at the top of the Talero ATS summariser prompt
The rules at the top of the summariser prompt: recommendation only, no protected characteristics, human review required.

Running it in production

  • Every code push runs lint, tests and a TypeScript build in GitHub Actions against real PostgreSQL (pgvector) and Redis containers.
  • Two checks run before any deploy: one renders the Docker Compose file with empty settings to catch missing defaults, and one starts the environment schema with production-shaped values. Both exist because an empty setting once put production into a crash loop.
  • CI builds the backend image once, tags it with the commit and the server pulls it, so a deploy takes about four minutes with no downtime. The large AI image only rebuilds when the AI code changes.
  • In June 2026 a deploy left the API hung for 31 minutes. I added health checks and a watchdog that now restarts a stuck container within about 45 seconds.
  • Calls to Vertex go through a circuit breaker that pauses for 60 seconds after five failures, and Sentry tags every error with the release it came from.
github.com · Actions
GitHub Actions runs of the Talero ATS CI/CD pipeline
Every change goes through the same test and deploy pipeline.

What I would do differently

I would measure AI cost per candidate from the first day. The pipeline logs token counts for each CV, but I never turned them into a cost per candidate, so today I can't give a reliable figure.

Stack

API
Node.js 22 · TypeScript · Express · Zod
Queues
BullMQ on Redis (CV parsing, scoring, AI evaluation)
AI pipeline
Python · FastAPI · Gemini on Vertex AI (EU) · local all-MiniLM-L6-v2 embeddings
Data
PostgreSQL + pgvector · Supabase (Auth, Storage, row-level security)
Frontend
React · TypeScript · Vite · Tailwind CSS · TanStack Query
Realtime
Server-sent events for recruiter dashboards
Infra
Docker Compose · Caddy · Google Cloud VM · GitHub Actions · GitHub Container Registry
Monitoring
Sentry releases per commit · Pino logs · autoheal watchdog

More work