Een assessmentaanbieder die wil koppelen met Workable of Carerix bouwt vijf onderdelen. Veilige authenticatie met API-tokens, een vaste mapping tussen kandidaat- en vacature-ID's, uitnodigingen die automatisch vertrekken zodra een kandidaat de assessmentfase in gaat, resultaten die terugkomen op het kandidaatprofiel in het ATS, en ondertekende, idempotente webhooks. Daaromheen moet de gegevensverwerking voldoen aan de AVG en, zodra er AI in de scoring zit, aan de EU AI Act.
Dit artikel is een technisch naslagwerk voor de developers die de koppelingslaag bouwen. Het gaat over authenticatie, ID-mapping, het starten van uitnodigingen, het synchroniseren van resultaten, webhookbeveiliging en gegevensverwerking volgens de AVG. Waarom een goede ATS-koppeling voor skilltests zo veel uitmaakt voor recruiters, lees je in een apart artikel.
Welk ATS je ook kiest, de levenscyclus van een koppeling is steeds dezelfde.
Het ATS is de bron van waarheid voor alle kandidaat- en vacaturegegevens. Jouw platform beheert alleen de status van de assessmentsessie en kopieert geen kandidaatprofielen.
Controleer deze punten voordat je een regel code schrijft.
candidate_id, job_id en je eigen invitation_id bewaart en bij elk binnenkomend event kunt opzoekenDe Assessment Provider API van Workable werkt met een statisch access token in de HTTP-header Authorization. Volgens de developerdocumentatie 'Assessment Providers' van Workable (workable.readme.io) wordt elke call van Workable naar jouw endpoint op deze manier geauthenticeerd.
Authorization: Bearer <your_access_token>
Je krijgt dit token zodra je koppeling via het partnerprogramma van Workable is ingericht. Vervang het token minstens elke 90 dagen, en direct bij een vermoeden van misbruik. Bewaar het in een secrets manager (AWS Secrets Manager, HashiCorp Vault of vergelijkbaar) en niet in omgevingsvariabelen op gedeelde servers.
Valideer het token aan jouw kant bij elk request, nog voordat je een payload verwerkt. Geef 401 Unauthorized terug bij een ontbrekend of ongeldig token. De foutafhandeling van Workable verwacht statuscodes als 400, 401, 409 en 422 met een gestructureerde foutmelding.
{
"error": {
"code": "INVALID_TOKEN",
"message": "The provided bearer token is not recognized."
}
}
Carerix bouwt koppelingen via een officieel partnerprogramma en stelt voor goedgekeurde partners een GraphQL API beschikbaar. Het precieze authenticatiemodel (OAuth 2.0 client credentials of een API-key) wordt bevestigd tijdens de partneronboarding. Bouw je een nieuwe connector, begin dan bij de pagina 'Become an Integration Partner' van Carerix. Daar staan het aanmeldproces en de verwachte doorlooptijd.
Ga er in de praktijk van uit dat je GraphQL-mutations gebruikt om te schrijven (een assessmentuitnodiging aanmaken, een kandidaat bijwerken) en queries om te lezen (kandidaat- of vacaturegegevens ophalen). Al het verkeer loopt minimaal via TLS 1.2.
Een strakke mapping voorkomt verweesde assessmentsessies en dubbele uitnodigingen. Deze ID-paren moet je opslag altijd bijhouden.
| Workable-veld | Veld in jouw platform | Toelichting |
|---|---|---|
candidate.id | applicant_id | Primaire sleutel voor alle kandidaatacties |
job.shortcode | role_id | Verwijst naar het assessmenttemplate voor die functie |
stage.name | trigger_stage | De fase die de uitnodiging start |
invitation_id (jouw veld) | session_id | Opslaan en in elke resultaatpayload meesturen |
Voor de uitnodiging heb je alleen de velden nodig om het assessment te versturen, namelijk first_name, last_name, email en language. Stuur velden als cover_letter, resume_url, cv-tekst of antwoorden op eigen screeningsvragen niet door, tenzij je koppeling ze echt nodig heeft en de kandidaat voor dat delen toestemming heeft gegeven.
In het GraphQL-schema van Carerix heten de vergelijkbare objecten Candidate en Vacancy. Map ze zo.
Candidate.id naar je applicant_idVacancy.id naar je role_idinvitation_id die jouw platform teruggeeftCarerix gebruikt GUID's in tekstvorm als objectidentificatie. Laat de exacte veldnamen bevestigen tijdens de partneronboarding, want het GraphQL-schema verandert mee met platformupdates.
Stuur alleen wat nodig is. Voor de uitnodiging is email vereist, met optioneel first_name en preferred_language. Al het andere (geboortedatum, adres, nationaliteit, antwoorden op eigen vragenlijsten) blijft in het ATS. Jouw platform slaat die velden nooit op, ook niet als ze in de inkomende payload staan.
Workable ondersteunt abonnementen op kandidaatevents via het endpoint /subscriptions. Je registreert een webhook met filters op fasewijzigingen. Met de subscription args filter je op eventtype, zodat je alleen candidate_moved of een vergelijkbaar event voor faseovergangen ontvangt en niet elke kandidaatactiviteit.
Gaat een kandidaat naar de ingestelde assessmentfase, dan stuurt Workable een POST naar je geregistreerde callback-URL. De payload bevat minimaal dit.
{
"event_type": "candidate_moved",
"event_id": "evt_01HXYZ",
"candidate": {
"id": "cand_7890",
"name": "Alex van der Berg",
"email": "[email protected]"
},
"job": {
"id": "job_4567",
"shortcode": "DEV001"
},
"stage": {
"name": "Assessment",
"previous_name": "Phone Screen"
}
}
Bij dit event zoekt je service het assessmenttemplate op dat aan job.shortcode hangt, maakt een uitnodiging aan en slaat de invitation_id op bij candidate.id + job.id. Antwoord binnen 5 seconden met 200 OK en doe het zware werk asynchroon.
Je call voor het aanmaken van een uitnodiging accepteert minimaal deze velden.
applicant_id (gemapt vanaf het kandidaat-ID in het ATS)role_id (gemapt vanaf het vacature-ID in het ATS)emaillanguage (standaard en als de taal onbekend is)assessment_template_id (bepaald via je configuratie van fase naar template)Deze optionele parameters wil je per klant kunnen instellen.
allow_retake (boolean, standaard false)proctoring_enabled (boolean)expiry_hours (integer, bijvoorbeeld 72)Wordt de kandidaat uit de assessmentfase teruggezet of afgewezen, dan doet je koppeling drie dingen.
invitation_id bestaat voor het paar candidate_id + job_idKomt de kandidaat later opnieuw in die fase, maak dan een nieuwe invitation_id aan en archiveer de oude. Hergebruik nooit een uitnodigings-ID. Dwing dat af met een unique constraint in de database.
Ondersteun zo mogelijk beide. Push (jouw platform stuurt resultaten met een POST naar een callback die bij Workable is geregistreerd) heeft de voorkeur omdat het sneller is, maar sommige ATS-inrichtingen werken met polling. Volgens de Assessment Providers-documentatie van Workable kunnen resultaten via een callback of via polling binnenkomen.
Een standaard resultaatpayload van jouw platform bevat minimaal dit.
{
"event_type": "assessment_completed",
"event_id": "res_evt_9921",
"invitation_id": "inv_3344",
"attempt_id": "att_5566",
"candidate_id": "cand_7890",
"job_id": "job_4567",
"status": "completed",
"completed_at": "2026-09-09T14:33:00Z",
"scores": {
"overall": 72,
"dimensions": {
"cognitive_ability": 68,
"conscientiousness": 81,
"verbal_reasoning": 74
}
},
"report_url": "https://app.assessmentplatform.com/reports/att_5566"
}
Gebruik voor het veld status een vaste woordenlijst, namelijk completed, abandoned, expired en failed. Het ATS vertaalt die naar eigen labels. Je contract mag nooit leunen op vrije tekst als status.
Voor Workable stuur je de resultaatpayload met een POST naar de callback-URL die bij het aanmaken van het assessment is meegegeven, of via het resultatenendpoint van de Assessment Provider API. Workable toont de scores daarna op het kandidaatprofiel. Zo werkt het ook bij aanbieders als Test Partnership, waarvan de resultaten op het Workable-profiel van de kandidaat verschijnen.
Voor Carerix werk je het kandidaatrecord bij met een GraphQL-mutation, via een eigen assessmentveld of een gestructureerde notitie met de scores en de rapport-URL.
Registreer je callback-URL via het endpoint /subscriptions van Workable. Volgens de webhookdocumentatie van Workable filteren de subscription args de eventstroom. Stel ze dus zo in dat je alleen events ontvangt die voor jouw koppeling relevant zijn, zoals fasewijzigingen bij vacatures waar je assessmenttemplate actief is.
Elke webhook van jouw platform naar het ATS en elke callback van het ATS naar jouw endpoint moet ondertekend zijn. Gebruik daarvoor HMAC-SHA256.
import hmac
import hashlib
def verify_signature(payload_bytes: bytes, received_sig: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received_sig)
Stuur een header X-Webhook-Timestamp mee en weiger payloads waarvan het tijdstempel ouder is dan 5 minuten. Zo voorkom je replay-aanvallen.
Workable en de meeste andere ATS'en proberen mislukte webhooks opnieuw af te leveren. Je endpoint moet daarom idempotent zijn. Hetzelfde event_id twee keer verwerken geeft dezelfde uitkomst, zonder bijeffecten zoals dubbele uitnodigingen of dubbel weggeschreven resultaten.
Zo pak je dat aan.
event_id weg in een deduplicatietabel met een TTL van minstens 24 uurevent_id al, geef dan direct 200 OK terug zonder te verwerkencurl -X POST https://callback.workable.com/assessment-results \
-H "Authorization: Bearer <your_access_token>" \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=<hmac_value>" \
-H "X-Webhook-Timestamp: 2026-09-09T14:33:01Z" \
-d '{
"invitation_id": "inv_3344",
"candidate_id": "cand_7890",
"status": "completed",
"scores": { "overall": 72 }
}'
curl -X GET https://api.assessmentplatform.com/v1/templates \
-H "Authorization: Bearer <your_access_token>"
Het antwoord ziet er zo uit.
{
"templates": [
{ "id": "tmpl_001", "name": "Cognitive + Personality Bundle", "language": "en" },
{ "id": "tmpl_002", "name": "Logistiek instapniveau NL", "language": "nl" }
]
}
curl -X POST https://api.assessmentplatform.com/v1/invitations \
-H "Authorization: Bearer <your_access_token>" \
-H "Content-Type: application/json" \
-d '{
"applicant_id": "cand_7890",
"role_id": "job_4567",
"template_id": "tmpl_001",
"email": "[email protected]",
"first_name": "Alex",
"language": "en",
"expiry_hours": 72
}'
Het antwoord ziet er zo uit.
{
"invitation_id": "inv_3344",
"status": "pending",
"invitation_url": "https://app.assessmentplatform.com/start/inv_3344",
"expires_at": "2026-09-12T14:33:00Z"
}
const crypto = require("crypto");
const express = require("express");
const app = express();
app.use(express.raw({ type: "application/json" }));
app.post("/webhooks/ats", (req, res) => {
const sig = req.headers["x-webhook-signature"];
const ts = req.headers["x-webhook-timestamp"];
const age = Date.now() - new Date(ts).getTime();
if (age > 5 * 60 * 1000) return res.status(400).send("Timestamp too old");
const expected = crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(req.body)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.replace("sha256=", "")))) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body);
// Idempotency check, then enqueue for processing
res.sendStatus(200);
});
Loop elk scenario door in een stagingomgeving voordat je productieverkeer aanzet.
| Testgeval | Verwachte uitkomst |
|---|---|
| Geldig token, correcte payload | 200 OK, uitnodiging aangemaakt |
| Verlopen of ongeldig token | 401 Unauthorized, geen uitnodiging aangemaakt |
| Kandidaat-ID niet gevonden in het ATS | 404 Not Found, fout gelogd |
| Vacature of fase niet gekoppeld aan een template | 422 Unprocessable Entity, alert verstuurd |
Dubbel event_id ontvangen | 200 OK, geen dubbele uitnodiging |
| Webhookhandtekening klopt niet | 401 Unauthorized, request geweigerd |
| Push van resultaten loopt vast (5xx van het ATS) | Opnieuw proberen met exponential backoff |
| Kandidaat afgewezen tijdens het assessment | Uitnodiging geannuleerd, sessie verlopen |
| Resultaatevent komt binnen vóór het uitnodigingsrecord | 30 seconden in de wachtrij houden, daarna opnieuw opzoeken |
| Status | Symptoom | Waarschijnlijke oorzaak | Oplossing |
|---|---|---|---|
401 | Alle requests geweigerd | Ongeldig of vervangen bearer token | Nieuw token aanmaken via de partneromgeving en de synchronisatie van de secrets manager controleren |
404 | Uitnodiging aanmaken mislukt | candidate_id of job_id staat niet in je mappingopslag | Nagaan of de webhook het fasewijzigingsevent heeft ontvangen en de deduplicatietabel op gemiste events controleren |
409 | Dubbele uitnodiging geweigerd | Dezelfde candidate_id + job_id heeft al een actieve uitnodiging | De bestaande eerst annuleren, of de bestaande invitation_id teruggeven |
422 | Validatiefout in de payload | Verplicht veld ontbreekt of ongeldige enum-waarde | De foutmelding per veld bekijken en de status-woordenlijst naast de specificatie leggen |
5xx | Callback voor resultaten faalt | ATS-endpoint onbereikbaar of verkeerd ingestelde callback-URL | Partnersupport van Workable inschakelen en de resultaten in de dead-letter queue houden tot het is opgelost |
Staan mislukte leveringen na 24 uur nog in de dead-letter queue, dan heeft je beheeromgeving een knop nodig om ze per invitation_id handmatig opnieuw te versturen, plus een export van openstaande resultaatpayloads om ze zo nodig handmatig in te voeren.
Om een uitnodiging aan te maken hoeven maar drie velden van het ATS naar je assessmentplatform, namelijk email, first_name (voor de aanhef) en preferred_language. Al het andere blijft in het ATS. Kan je platform niet zonder extra velden, leg dan eerst de grondslag vast volgens artikel 6 van de AVG voordat je die overdracht aanzet.
Sla geen ruwe cv-tekst, antwoorden op screeningsvragen of beoordelingsnotities van recruiters op. Die velden kunnen in de webhookpayloads van sommige ATS'en zitten. Filter ze eruit bij binnenkomst, voordat ze je database bereiken.
Leg twee toestemmingsmomenten vast in je audittrail, elk met een UTC-tijdstempel.
Leg naast het tijdstempel ook vast hoe de toestemming is gegeven, bijvoorbeeld via een toestemmingsveld in het ATS of een vinkje in het product.
Werk je voor werkgevers in de EU, sla de persoonsgegevens uit je koppeling dan binnen de EU op. Selection Lab slaat bijvoorbeeld alle persoonsgegevens op in Frankfurt en haalt met lokale taalmodellen persoonlijke informatie uit gespreksdata voordat die bij een scoringsmodel komt. Dat patroon is het kopiëren waard. Laat elk AI-onderdeel in de scoring waar technisch mogelijk werken met geanonimiseerde of gepseudonimiseerde input.
Stel bewaartermijnen per klant in en laat ze automatisch handhaven. Een redelijke standaard is 12 maanden na afronding van het assessment, waarna de gegevens worden verwijderd of geanonimiseerd. Klanten in gereguleerde sectoren willen soms een kortere termijn. Bouw de termijn daarom als instelbare parameter en niet als vaste waarde in de code.
Gebruikt een onderdeel van je assessmentplatform een AI-model om scores of aanbevelingen te genereren, dan valt het in de regel onder de AI-systemen met een hoog risico uit bijlage III van de EU AI Act (werving en selectie). Dat vraagt om deze maatregelen.
Selection Lab combineert daarom transparante scorerapporten per dimensie met uitlegbare AI-uitkomsten die recruiters direct in de kandidaatweergave van hun ATS bekijken. Die herleidbaarheid is wat compliance met de EU AI Act op koppelingsniveau concreet vraagt. Elke automatische score die een aannamebesluit beïnvloedt, moet van begin tot eind te controleren zijn.
Een volledige koppeling met authenticatie, ID-mapping, uitnodigingen vanuit fasewijzigingen, synchronisatie van resultaten, webhookbeveiliging en AVG-proof gegevensverwerking kost doorgaans 2 tot 10 weken. Dat hangt af van de complexiteit van de ATS-omgeving en het aantal assessmenttemplates dat je inricht. Plan daarbinnen minstens een week voor end-to-end testen tegen een staging-omgeving van het ATS, voordat je productieverkeer aanzet.
Wil je zien hoe de ATS-koppelingen van Selection Lab in de praktijk werken? Vraag een demo aan.
Via de Assessment Provider API van Workable, waarvoor je je eerst aanmeldt bij het Workable Partner Program. Je registreert een webhook op fasewijzigingen via het endpoint /subscriptions, maakt een uitnodiging aan voor elke kandidaat die de assessmentfase in gaat en stuurt de resultaten terug naar de callback-URL. De scores verschijnen dan op het kandidaatprofiel in Workable.
Ja. Carerix stelt een GraphQL API beschikbaar voor goedgekeurde integratiepartners. Je gebruikt mutations om uitnodigingen en resultaten weg te schrijven en queries om kandidaat- en vacaturegegevens te lezen. Het precieze authenticatiemodel en de veldnamen worden bevestigd tijdens de partneronboarding.
Alleen wat nodig is om de uitnodiging te versturen, dus het e-mailadres, de voornaam voor de aanhef en de voorkeurstaal. Cv-tekst, antwoorden op screeningsvragen, geboortedatum en adres blijven in het ATS. Heb je meer velden nodig, leg dan eerst de grondslag vast onder artikel 6 van de AVG.
In de regel wel. AI-systemen die kandidaten beoordelen of filteren bij werving en selectie staan in bijlage III van de EU AI Act en gelden als hoog risico. Dat betekent onder meer logging van elke scoring met modelversie, uitleg per scoredimensie, gedocumenteerde biastests en een conformiteitslog.
Doorgaans 2 tot 10 weken, afhankelijk van de complexiteit van de ATS-omgeving en het aantal assessmenttemplates. Plan daarbinnen minstens een week voor end-to-end testen tegen een staging-omgeving van het ATS voordat je live gaat.

Een assessmentaanbieder die wil koppelen met Workable of Carerix bouwt vijf onderdelen. Veilige authenticatie met API-tokens, een vaste mapping tussen kandidaat- en vacature-ID's, uitnodigingen die automatisch vertrekken zodra een kandidaat de assessmentfase in gaat, resultaten die terugkomen op het kandidaatprofiel in het ATS, en ondertekende, idempotente webhooks. Daaromheen moet de gegevensverwerking voldoen aan de AVG en, zodra er AI in de scoring zit, aan de EU AI Act.
Dit artikel is een technisch naslagwerk voor de developers die de koppelingslaag bouwen. Het gaat over authenticatie, ID-mapping, het starten van uitnodigingen, het synchroniseren van resultaten, webhookbeveiliging en gegevensverwerking volgens de AVG. Waarom een goede ATS-koppeling voor skilltests zo veel uitmaakt voor recruiters, lees je in een apart artikel.
Welk ATS je ook kiest, de levenscyclus van een koppeling is steeds dezelfde.
Het ATS is de bron van waarheid voor alle kandidaat- en vacaturegegevens. Jouw platform beheert alleen de status van de assessmentsessie en kopieert geen kandidaatprofielen.
Controleer deze punten voordat je een regel code schrijft.
candidate_id, job_id en je eigen invitation_id bewaart en bij elk binnenkomend event kunt opzoekenDe Assessment Provider API van Workable werkt met een statisch access token in de HTTP-header Authorization. Volgens de developerdocumentatie 'Assessment Providers' van Workable (workable.readme.io) wordt elke call van Workable naar jouw endpoint op deze manier geauthenticeerd.
Authorization: Bearer <your_access_token>
Je krijgt dit token zodra je koppeling via het partnerprogramma van Workable is ingericht. Vervang het token minstens elke 90 dagen, en direct bij een vermoeden van misbruik. Bewaar het in een secrets manager (AWS Secrets Manager, HashiCorp Vault of vergelijkbaar) en niet in omgevingsvariabelen op gedeelde servers.
Valideer het token aan jouw kant bij elk request, nog voordat je een payload verwerkt. Geef 401 Unauthorized terug bij een ontbrekend of ongeldig token. De foutafhandeling van Workable verwacht statuscodes als 400, 401, 409 en 422 met een gestructureerde foutmelding.
{
"error": {
"code": "INVALID_TOKEN",
"message": "The provided bearer token is not recognized."
}
}
Carerix bouwt koppelingen via een officieel partnerprogramma en stelt voor goedgekeurde partners een GraphQL API beschikbaar. Het precieze authenticatiemodel (OAuth 2.0 client credentials of een API-key) wordt bevestigd tijdens de partneronboarding. Bouw je een nieuwe connector, begin dan bij de pagina 'Become an Integration Partner' van Carerix. Daar staan het aanmeldproces en de verwachte doorlooptijd.
Ga er in de praktijk van uit dat je GraphQL-mutations gebruikt om te schrijven (een assessmentuitnodiging aanmaken, een kandidaat bijwerken) en queries om te lezen (kandidaat- of vacaturegegevens ophalen). Al het verkeer loopt minimaal via TLS 1.2.
Een strakke mapping voorkomt verweesde assessmentsessies en dubbele uitnodigingen. Deze ID-paren moet je opslag altijd bijhouden.
| Workable-veld | Veld in jouw platform | Toelichting |
|---|---|---|
candidate.id | applicant_id | Primaire sleutel voor alle kandidaatacties |
job.shortcode | role_id | Verwijst naar het assessmenttemplate voor die functie |
stage.name | trigger_stage | De fase die de uitnodiging start |
invitation_id (jouw veld) | session_id | Opslaan en in elke resultaatpayload meesturen |
Voor de uitnodiging heb je alleen de velden nodig om het assessment te versturen, namelijk first_name, last_name, email en language. Stuur velden als cover_letter, resume_url, cv-tekst of antwoorden op eigen screeningsvragen niet door, tenzij je koppeling ze echt nodig heeft en de kandidaat voor dat delen toestemming heeft gegeven.
In het GraphQL-schema van Carerix heten de vergelijkbare objecten Candidate en Vacancy. Map ze zo.
Candidate.id naar je applicant_idVacancy.id naar je role_idinvitation_id die jouw platform teruggeeftCarerix gebruikt GUID's in tekstvorm als objectidentificatie. Laat de exacte veldnamen bevestigen tijdens de partneronboarding, want het GraphQL-schema verandert mee met platformupdates.
Stuur alleen wat nodig is. Voor de uitnodiging is email vereist, met optioneel first_name en preferred_language. Al het andere (geboortedatum, adres, nationaliteit, antwoorden op eigen vragenlijsten) blijft in het ATS. Jouw platform slaat die velden nooit op, ook niet als ze in de inkomende payload staan.
Workable ondersteunt abonnementen op kandidaatevents via het endpoint /subscriptions. Je registreert een webhook met filters op fasewijzigingen. Met de subscription args filter je op eventtype, zodat je alleen candidate_moved of een vergelijkbaar event voor faseovergangen ontvangt en niet elke kandidaatactiviteit.
Gaat een kandidaat naar de ingestelde assessmentfase, dan stuurt Workable een POST naar je geregistreerde callback-URL. De payload bevat minimaal dit.
{
"event_type": "candidate_moved",
"event_id": "evt_01HXYZ",
"candidate": {
"id": "cand_7890",
"name": "Alex van der Berg",
"email": "[email protected]"
},
"job": {
"id": "job_4567",
"shortcode": "DEV001"
},
"stage": {
"name": "Assessment",
"previous_name": "Phone Screen"
}
}
Bij dit event zoekt je service het assessmenttemplate op dat aan job.shortcode hangt, maakt een uitnodiging aan en slaat de invitation_id op bij candidate.id + job.id. Antwoord binnen 5 seconden met 200 OK en doe het zware werk asynchroon.
Je call voor het aanmaken van een uitnodiging accepteert minimaal deze velden.
applicant_id (gemapt vanaf het kandidaat-ID in het ATS)role_id (gemapt vanaf het vacature-ID in het ATS)emaillanguage (standaard en als de taal onbekend is)assessment_template_id (bepaald via je configuratie van fase naar template)Deze optionele parameters wil je per klant kunnen instellen.
allow_retake (boolean, standaard false)proctoring_enabled (boolean)expiry_hours (integer, bijvoorbeeld 72)Wordt de kandidaat uit de assessmentfase teruggezet of afgewezen, dan doet je koppeling drie dingen.
invitation_id bestaat voor het paar candidate_id + job_idKomt de kandidaat later opnieuw in die fase, maak dan een nieuwe invitation_id aan en archiveer de oude. Hergebruik nooit een uitnodigings-ID. Dwing dat af met een unique constraint in de database.
Ondersteun zo mogelijk beide. Push (jouw platform stuurt resultaten met een POST naar een callback die bij Workable is geregistreerd) heeft de voorkeur omdat het sneller is, maar sommige ATS-inrichtingen werken met polling. Volgens de Assessment Providers-documentatie van Workable kunnen resultaten via een callback of via polling binnenkomen.
Een standaard resultaatpayload van jouw platform bevat minimaal dit.
{
"event_type": "assessment_completed",
"event_id": "res_evt_9921",
"invitation_id": "inv_3344",
"attempt_id": "att_5566",
"candidate_id": "cand_7890",
"job_id": "job_4567",
"status": "completed",
"completed_at": "2026-09-09T14:33:00Z",
"scores": {
"overall": 72,
"dimensions": {
"cognitive_ability": 68,
"conscientiousness": 81,
"verbal_reasoning": 74
}
},
"report_url": "https://app.assessmentplatform.com/reports/att_5566"
}
Gebruik voor het veld status een vaste woordenlijst, namelijk completed, abandoned, expired en failed. Het ATS vertaalt die naar eigen labels. Je contract mag nooit leunen op vrije tekst als status.
Voor Workable stuur je de resultaatpayload met een POST naar de callback-URL die bij het aanmaken van het assessment is meegegeven, of via het resultatenendpoint van de Assessment Provider API. Workable toont de scores daarna op het kandidaatprofiel. Zo werkt het ook bij aanbieders als Test Partnership, waarvan de resultaten op het Workable-profiel van de kandidaat verschijnen.
Voor Carerix werk je het kandidaatrecord bij met een GraphQL-mutation, via een eigen assessmentveld of een gestructureerde notitie met de scores en de rapport-URL.
Registreer je callback-URL via het endpoint /subscriptions van Workable. Volgens de webhookdocumentatie van Workable filteren de subscription args de eventstroom. Stel ze dus zo in dat je alleen events ontvangt die voor jouw koppeling relevant zijn, zoals fasewijzigingen bij vacatures waar je assessmenttemplate actief is.
Elke webhook van jouw platform naar het ATS en elke callback van het ATS naar jouw endpoint moet ondertekend zijn. Gebruik daarvoor HMAC-SHA256.
import hmac
import hashlib
def verify_signature(payload_bytes: bytes, received_sig: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
payload_bytes,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, received_sig)
Stuur een header X-Webhook-Timestamp mee en weiger payloads waarvan het tijdstempel ouder is dan 5 minuten. Zo voorkom je replay-aanvallen.
Workable en de meeste andere ATS'en proberen mislukte webhooks opnieuw af te leveren. Je endpoint moet daarom idempotent zijn. Hetzelfde event_id twee keer verwerken geeft dezelfde uitkomst, zonder bijeffecten zoals dubbele uitnodigingen of dubbel weggeschreven resultaten.
Zo pak je dat aan.
event_id weg in een deduplicatietabel met een TTL van minstens 24 uurevent_id al, geef dan direct 200 OK terug zonder te verwerkencurl -X POST https://callback.workable.com/assessment-results \
-H "Authorization: Bearer <your_access_token>" \
-H "Content-Type: application/json" \
-H "X-Webhook-Signature: sha256=<hmac_value>" \
-H "X-Webhook-Timestamp: 2026-09-09T14:33:01Z" \
-d '{
"invitation_id": "inv_3344",
"candidate_id": "cand_7890",
"status": "completed",
"scores": { "overall": 72 }
}'
curl -X GET https://api.assessmentplatform.com/v1/templates \
-H "Authorization: Bearer <your_access_token>"
Het antwoord ziet er zo uit.
{
"templates": [
{ "id": "tmpl_001", "name": "Cognitive + Personality Bundle", "language": "en" },
{ "id": "tmpl_002", "name": "Logistiek instapniveau NL", "language": "nl" }
]
}
curl -X POST https://api.assessmentplatform.com/v1/invitations \
-H "Authorization: Bearer <your_access_token>" \
-H "Content-Type: application/json" \
-d '{
"applicant_id": "cand_7890",
"role_id": "job_4567",
"template_id": "tmpl_001",
"email": "[email protected]",
"first_name": "Alex",
"language": "en",
"expiry_hours": 72
}'
Het antwoord ziet er zo uit.
{
"invitation_id": "inv_3344",
"status": "pending",
"invitation_url": "https://app.assessmentplatform.com/start/inv_3344",
"expires_at": "2026-09-12T14:33:00Z"
}
const crypto = require("crypto");
const express = require("express");
const app = express();
app.use(express.raw({ type: "application/json" }));
app.post("/webhooks/ats", (req, res) => {
const sig = req.headers["x-webhook-signature"];
const ts = req.headers["x-webhook-timestamp"];
const age = Date.now() - new Date(ts).getTime();
if (age > 5 * 60 * 1000) return res.status(400).send("Timestamp too old");
const expected = crypto
.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(req.body)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.replace("sha256=", "")))) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body);
// Idempotency check, then enqueue for processing
res.sendStatus(200);
});
Loop elk scenario door in een stagingomgeving voordat je productieverkeer aanzet.
| Testgeval | Verwachte uitkomst |
|---|---|
| Geldig token, correcte payload | 200 OK, uitnodiging aangemaakt |
| Verlopen of ongeldig token | 401 Unauthorized, geen uitnodiging aangemaakt |
| Kandidaat-ID niet gevonden in het ATS | 404 Not Found, fout gelogd |
| Vacature of fase niet gekoppeld aan een template | 422 Unprocessable Entity, alert verstuurd |
Dubbel event_id ontvangen | 200 OK, geen dubbele uitnodiging |
| Webhookhandtekening klopt niet | 401 Unauthorized, request geweigerd |
| Push van resultaten loopt vast (5xx van het ATS) | Opnieuw proberen met exponential backoff |
| Kandidaat afgewezen tijdens het assessment | Uitnodiging geannuleerd, sessie verlopen |
| Resultaatevent komt binnen vóór het uitnodigingsrecord | 30 seconden in de wachtrij houden, daarna opnieuw opzoeken |
| Status | Symptoom | Waarschijnlijke oorzaak | Oplossing |
|---|---|---|---|
401 | Alle requests geweigerd | Ongeldig of vervangen bearer token | Nieuw token aanmaken via de partneromgeving en de synchronisatie van de secrets manager controleren |
404 | Uitnodiging aanmaken mislukt | candidate_id of job_id staat niet in je mappingopslag | Nagaan of de webhook het fasewijzigingsevent heeft ontvangen en de deduplicatietabel op gemiste events controleren |
409 | Dubbele uitnodiging geweigerd | Dezelfde candidate_id + job_id heeft al een actieve uitnodiging | De bestaande eerst annuleren, of de bestaande invitation_id teruggeven |
422 | Validatiefout in de payload | Verplicht veld ontbreekt of ongeldige enum-waarde | De foutmelding per veld bekijken en de status-woordenlijst naast de specificatie leggen |
5xx | Callback voor resultaten faalt | ATS-endpoint onbereikbaar of verkeerd ingestelde callback-URL | Partnersupport van Workable inschakelen en de resultaten in de dead-letter queue houden tot het is opgelost |
Staan mislukte leveringen na 24 uur nog in de dead-letter queue, dan heeft je beheeromgeving een knop nodig om ze per invitation_id handmatig opnieuw te versturen, plus een export van openstaande resultaatpayloads om ze zo nodig handmatig in te voeren.
Om een uitnodiging aan te maken hoeven maar drie velden van het ATS naar je assessmentplatform, namelijk email, first_name (voor de aanhef) en preferred_language. Al het andere blijft in het ATS. Kan je platform niet zonder extra velden, leg dan eerst de grondslag vast volgens artikel 6 van de AVG voordat je die overdracht aanzet.
Sla geen ruwe cv-tekst, antwoorden op screeningsvragen of beoordelingsnotities van recruiters op. Die velden kunnen in de webhookpayloads van sommige ATS'en zitten. Filter ze eruit bij binnenkomst, voordat ze je database bereiken.
Leg twee toestemmingsmomenten vast in je audittrail, elk met een UTC-tijdstempel.
Leg naast het tijdstempel ook vast hoe de toestemming is gegeven, bijvoorbeeld via een toestemmingsveld in het ATS of een vinkje in het product.
Werk je voor werkgevers in de EU, sla de persoonsgegevens uit je koppeling dan binnen de EU op. Selection Lab slaat bijvoorbeeld alle persoonsgegevens op in Frankfurt en haalt met lokale taalmodellen persoonlijke informatie uit gespreksdata voordat die bij een scoringsmodel komt. Dat patroon is het kopiëren waard. Laat elk AI-onderdeel in de scoring waar technisch mogelijk werken met geanonimiseerde of gepseudonimiseerde input.
Stel bewaartermijnen per klant in en laat ze automatisch handhaven. Een redelijke standaard is 12 maanden na afronding van het assessment, waarna de gegevens worden verwijderd of geanonimiseerd. Klanten in gereguleerde sectoren willen soms een kortere termijn. Bouw de termijn daarom als instelbare parameter en niet als vaste waarde in de code.
Gebruikt een onderdeel van je assessmentplatform een AI-model om scores of aanbevelingen te genereren, dan valt het in de regel onder de AI-systemen met een hoog risico uit bijlage III van de EU AI Act (werving en selectie). Dat vraagt om deze maatregelen.
Selection Lab combineert daarom transparante scorerapporten per dimensie met uitlegbare AI-uitkomsten die recruiters direct in de kandidaatweergave van hun ATS bekijken. Die herleidbaarheid is wat compliance met de EU AI Act op koppelingsniveau concreet vraagt. Elke automatische score die een aannamebesluit beïnvloedt, moet van begin tot eind te controleren zijn.
Een volledige koppeling met authenticatie, ID-mapping, uitnodigingen vanuit fasewijzigingen, synchronisatie van resultaten, webhookbeveiliging en AVG-proof gegevensverwerking kost doorgaans 2 tot 10 weken. Dat hangt af van de complexiteit van de ATS-omgeving en het aantal assessmenttemplates dat je inricht. Plan daarbinnen minstens een week voor end-to-end testen tegen een staging-omgeving van het ATS, voordat je productieverkeer aanzet.
Wil je zien hoe de ATS-koppelingen van Selection Lab in de praktijk werken? Vraag een demo aan.
Via de Assessment Provider API van Workable, waarvoor je je eerst aanmeldt bij het Workable Partner Program. Je registreert een webhook op fasewijzigingen via het endpoint /subscriptions, maakt een uitnodiging aan voor elke kandidaat die de assessmentfase in gaat en stuurt de resultaten terug naar de callback-URL. De scores verschijnen dan op het kandidaatprofiel in Workable.
Ja. Carerix stelt een GraphQL API beschikbaar voor goedgekeurde integratiepartners. Je gebruikt mutations om uitnodigingen en resultaten weg te schrijven en queries om kandidaat- en vacaturegegevens te lezen. Het precieze authenticatiemodel en de veldnamen worden bevestigd tijdens de partneronboarding.
Alleen wat nodig is om de uitnodiging te versturen, dus het e-mailadres, de voornaam voor de aanhef en de voorkeurstaal. Cv-tekst, antwoorden op screeningsvragen, geboortedatum en adres blijven in het ATS. Heb je meer velden nodig, leg dan eerst de grondslag vast onder artikel 6 van de AVG.
In de regel wel. AI-systemen die kandidaten beoordelen of filteren bij werving en selectie staan in bijlage III van de EU AI Act en gelden als hoog risico. Dat betekent onder meer logging van elke scoring met modelversie, uitleg per scoredimensie, gedocumenteerde biastests en een conformiteitslog.
Doorgaans 2 tot 10 weken, afhankelijk van de complexiteit van de ATS-omgeving en het aantal assessmenttemplates. Plan daarbinnen minstens een week voor end-to-end testen tegen een staging-omgeving van het ATS voordat je live gaat.