# Wiring interviews into your ATS: API and events — Criterio Talent

> A technical guide to integrating an interview platform with an ATS or HRIS: idempotency, signed events, signature verification, retries and shared vocabulary.

URL: https://criteriotalent.com/en/recursos/integrar-entrevistas-con-tu-ats/

---

**Issue 10 · June 2026**

# Wiring interviews into your ATS: the API, the events, and the mistakes that get expensive

Written for whoever implements it. Who is in charge, why the request that creates an interview has to be repeatable, how a signature is verified, and the mistake nobody sees coming: the same word with two meanings.

Integration · API and events

Published on 29 August 2026 · 7 min read

**In short**

You integrate by letting the client system remain the source of the role and the candidate, creating interviews with an idempotency key so a retry duplicates nothing, and receiving signed events instead of polling. The signature is verified with a constant-time comparison and with the timestamp inside the signed message. Before any code is written, both sides agree in writing on what each word means.

An interview integration looks very little like the one you imagine at the start. Almost nobody trips over the shape of the JSON. They trip over the network that dropped mid-request, the event that arrived twice, the receiver that was down for forty minutes, and the word both teams had been using for months believing it meant the same thing.

What follows is the list of decisions to make before writing the first line, ordered by what they cost to fix later. The specific operations that exist today are on [the integrations page](https://criteriotalent.com/en/integraciones/); this issue is about using them without getting hurt.

## The ATS is still in charge, and it is worth saying out loud

The first decision is about ownership, not architecture: who owns what. The role is born in the client system, and so is the candidate. The interview platform does not create them: it receives them, mirrors them, and returns what it produced about them.

That means syncing a role is an operation you can repeat without consequences — you send the current state and it lands — and that the identifier in charge is the one from the ATS. When each side has its own identifier and neither is authoritative, the first reconciliation is done by hand, and so is every one after it.

It is worth writing this sentence into the scope document: “the source of truth for the role and the candidate is the ATS; the source of truth for the interview, its transcript and its report is the platform”. Half an hour of argument there saves weeks afterwards.

## The request that creates has to be repeatable

This is the most common problem and the one that looks worst from outside. The ATS requests an interview for a person. The request goes out, the platform processes it, and the response is lost on the way back: a timeout, a load balancer that cut the connection, a corporate network doing what corporate networks do. The ATS does not know whether the interview exists, so it retries. And the person receives two links.

The fix is not retrying more carefully: it is making the retry incapable of duplicating anything. The request that creates carries an idempotency key, generated by the ATS and stable for that combination of candidate and role. If the key has been seen before, the platform returns the interview that already exists instead of creating a second one.

- The key is generated by the caller, not the receiver. Generated by the receiver it is useless: the side that retries does not have it.
- The key has to be stable across a retry and different across a legitimately new request. An identifier derived from the candidate and the role does both; a timestamp does neither.
- Store the key you used next to the ATS record. The day somebody has to audit why two interviews exist, that column is the answer.

## Events instead of polling

The temptation is to ask every minute whether the interview has finished. It works in the demo and degrades on its own: with a hundred active candidates that is a hundred questions a minute, and ninety-nine per cent of the answers say “not yet”.

Worse than the cost is the latency. Polling introduces, by definition, a delay equal to the interval, and that delay shows up exactly where it matters: the recruiter refreshes the ATS, sees a stale state, and calls support. An event arrives when the thing that had to happen happened.

The practical rule: events are the primary mechanism and direct queries are the safety net. One reconciliation query a day over interviews that have sat too long in the same state covers whatever a lost event might leave behind, without turning the integration into a permanent survey.

## Verifying the signature, and why the timestamp goes inside

An event receiver is a public address. If it does not verify who is talking to it, anyone who finds it can push false states into the ATS. Verification is short to write, and there are three details where it breaks.

- Sign the EXACT body, byte for byte, as it arrived. If your framework deserialises the JSON and you re-serialise it to verify, the signature will not match even when the content is identical: key order and whitespace change.
- Compare in constant time. An ordinary string comparison returns early when the first character differs, and that microsecond difference, measured enough times, lets someone guess the signature character by character.
- Reject a distant timestamp. A window of a few minutes against your own clock is enough, and it is what stops someone replaying a legitimate event captured weeks ago.

That third point has a design consequence people skip: the timestamp has to travel INSIDE the signed message, not merely alongside it. If it travels outside, whoever captured an old event replays it with the hour changed, and the signature — computed over the body alone — is still valid. Signing the concatenation of the timestamp and the body closes that hole.

## The receiver has to be idempotent too

A serious event system retries when it gets no acknowledgement. That means your receiver will see the same event twice, sooner or later, and not because anyone made a mistake: because your response was lost after you had already processed it.

Processing “the interview finished” twice is usually harmless. Processing “the report is ready” twice can fire two emails at the same recruiter, or move a stage in the ATS twice. The defence is storing the identifier of every event already processed and discarding the repeat, with a table and a uniqueness constraint. Nothing more sophisticated is needed.

Answer fast and work afterwards. A receiver that acknowledges as soon as it has stored the event, and does the heavy work in the background, does not provoke retries through slowness. A receiver that renders a PDF before answering does.

## When the other side does not answer

It will happen. Maintenance, an expired certificate, a deployment that took longer than planned. What separates a grown-up integration from a fragile one is having decided beforehand what happens then.

An example decision matrix. What matters is not this particular table, but that one exists before the first incident.

| Situation | What the sender should do | What the receiver should do | The receiver returns a temporary error | Retry with growing backoff, not in a tight loop | Return the real error, not an empty success | The receiver has been down for hours | Keep retrying inside a bounded window, then stop | Reconcile on return, querying whatever was left pending | A repeated event arrived | Nothing: the retry is correct | Discard it by its identifier and acknowledge anyway | The signature does not validate | Nothing | Reject, log the attempt, and do not process it

And one request almost nobody makes and everybody appreciates: a test event, fired by hand at the receiver to confirm the address answers and the signature validates, before a single real candidate is involved.

## The most expensive mistake: one word, two meanings

Everything above is fixable with code. This is not. In an integration meeting, somebody from the ATS side says something like this:

That discovery is cheap in week one and ruinous in week twelve, when dashboards have been built on top of it. The list of words to define in writing is short and always the same: role, candidate, application, interview, rejected, finished, active.

An illustrative example of the most frequent divergences. The real values on each side are agreed in writing before code is written.

| Word | What it usually means in an ATS | What it usually means in the interview | Role | The approved requisition, with budget | The script and competencies it will interview with | Candidate | The person, unique across the whole database | The person in the context of one specific role | Rejected | The recruiter dropped the person | The person declined the interview | Finished | The selection process closed | The conversation ended, even if the report is still coming

## What this does not solve

A well-built integration moves data between two systems without losing or duplicating it. It does not fix a selection process that was never clear, it does not make comparable a set of interviews run with different criteria — that lives earlier, in the script, and [the August issue](https://criteriotalent.com/en/recursos/entrevista-estructurada-con-ia/) deals with it — and it does not replace the scoping conversation where you decide which fields travel and in which direction.

Nor does it eliminate the reconciliation work of the first month. It makes it small and explainable, which is the most you can ask of an integration between two systems that had never met.

## Questions about this issue

### Should we start with the integration or with a few roles by hand?

A few by hand, almost always. The first weeks surface vocabulary and process mismatches that no document reveals, and it is far cheaper to find them before writing the code that maps them. The [buying checklist](https://criteriotalent.com/en/recursos/checklist-para-comprar-plataforma-de-entrevistas/) includes that question for the same reason.

### What if our ATS cannot receive incoming events?

Common in older or closed installations. The alternative is a periodic reconciliation query over active interviews, accepting the latency it introduces. It works; you simply have to choose the interval with your eyes open rather than discover it when somebody complains about a stale state.

### How much information should be returned to the ATS?

Less than is asked for at the start. The interview state and a link to the report usually suffice; pulling the full transcript into the ATS duplicates sensitive material into a system that may have a different retention policy. [The May issue](https://criteriotalent.com/en/recursos/retencion-y-gobierno-de-datos/) covers that.

**To keep reading on this site**

### [Solutions](https://criteriotalent.com/en/soluciones/)

How it is configured for high volume, technical profiles, leadership, and multi-round processes.

### [Platform](https://criteriotalent.com/en/plataforma/)

How it runs the interview, follows up, and cites the evidence behind each conclusion.

### [Integrations](https://criteriotalent.com/en/integraciones/)

How your ATS requests the interview and receives the report, with nobody retyping anything.

### [Contact](https://criteriotalent.com/en/contacto/)

A 30-minute demo on a real role of yours.

**Other issues**

**Method · Evidence and traceability**

### [Turn-level evidence: what to keep from an interview so you can still defend it six months later](https://criteriotalent.com/en/recursos/evidencia-por-turno-trazabilidad/)

The uncomfortable question does not arrive on decision day: it arrives half a year later, once the team has changed. This is what has to be stored to answer it.

**Buying · Checklist**

### [A checklist for buying an AI interview platform: 24 questions, and the answers that should worry you](https://criteriotalent.com/en/recursos/checklist-para-comprar-plataforma-de-entrevistas/)

Twenty-four questions in six groups. The useful part is not the questions — anyone has those — but what follows each group: which answers sound good and say nothing.

**Method · Interview design**

### [How to design a structured AI interview that leaves useful evidence and keeps the decision human](https://criteriotalent.com/en/recursos/entrevista-estructurada-con-ia/)

Eight design decisions, in order. The first is the one almost everyone skips: you do not ask a model to judge, you ask it to document.

**Next step**

## See it with a role of yours on the table.

Thirty minutes: an interview is defined from your job post, walked through the way the candidate sees it, and a report is read with its evidence.
