Process automation
Onboarding-automatisering herbouwd: 9 dagen naar 31 uur
Hun recruiters joegen twee keer per week dezelfde onboarding-tickets na. We herbouwden de flow rond één Temporal-worker. Contract-naar-payroll viel terug van negen dagen naar 31 uur.

Het is 16:40 op een dinsdag in maart. Een recruiter bij een uitzendbureau in Den Bosch (17 medewerkers) tekent een orderpicker. De kandidaat wil donderdagochtend beginnen. De recruiter weet dat dat niet gaat lukken. Zelfs als elk systeem meewerkt, is de eerste payroll-run nog negen dagen weg.
Dat vertelt ze de kandidaat niet. Ze zegt dat ze vrijdag contact opneemt over de papieren, opent dan een Trello-bord, zoekt de kolom "Nieuwe kandidaten" en zet er een kaart in. Onder de kaart hangt een checklist van 23 punten. Ze vinkt de eerste drie af en gaat naar huis.
Dat was de workflow die wij moesten herschrijven.
Wat die 23 stappen eigenlijk waren
Vraag een uitzendbureau hoe onboarding loopt en je krijgt meestal een zin met het woord "gewoon" erin. "We hoeven ze gewoon in het systeem te zetten, dan pakt payroll het op." Dan zit je een dag bij ze op kantoor en stort het aantal "gewoon"s in.
De echte lijst, zoals we 'm die eerste dinsdag op het whiteboard zetten, telde drie mensen en twintig systemen. ID-scan en verificatie. VOG aanvragen. Bankrekening opnemen. Formulier loonheffingenkorting. Pensioenregeling aanmelden. Arbo-instructie PDF met aftekening. Welkomstmail. Tijdregistratie-account. Loonadministratie in Loket. Klantportaal-account. ABU-check. Sectorspecifieke onboarding voor magazijn, keuken of zorg. Verzuimverzekering aanmelden. UWV-meldingen. Eerste dienst inplannen. Bevestiging terug naar de recruiter. En nog vijf dingen die alleen de office manager nog wist.
Geen enkel systeem praatte met een ander. Elke integratie was een eenrichtingsmail of een CSV-upload die iemand om 17:00 één keer per dag deed.
Waar die negen dagen echt naartoe gingen
Toen we de tijdlijn tegen de kalender legden, kwam de lange staart uit vier hoeken.
Ten eerste: de VOG. Justis hanteert een streeftermijn van een paar werkdagen voor digitale aanvragen via eHerkenning, en meerdere weken voor papier. Dit bureau diende in op papier, want zo deed de office manager het altijd al.
Ten tweede: de bankverificatie. De recruiter tikte het IBAN van de kandidaat in een spreadsheet, die om middernacht in het payroll-systeem werd geüpload. Stond er een typefout in het IBAN, dan kwam dat pas de volgende ochtend boven water, als de upload bounced.
Ten derde: de payroll-cutoff. De weekrun van Loket sluit op woensdag 14:00. Was je medewerker om 14:01 rond, dan wachtte hij een hele week.
Ten vierde: het Trello-bord. Het bord was de bron van waarheid. Wat betekende dat elke niet-afgevinkte stap óf gedaan en niet bijgehouden was, óf gewoon niet gedaan. Onmogelijk om te zien welke van de twee.
Waarom Temporal en niet een queue plus een database-tabel
Het instinct, als je dit soort processen ziet, is grijpen naar een job queue. Sidekiq, Bull, SQS. Bouw een state machine in je app, push jobs op een queue, bewaar de state in Postgres, en accepteer dat je het komende jaar bezig bent met retry-logica.
Dat patroon werkt. Tot de workflow halverwege een deploy moet overleven. Of een mens twee dagen doet over een goedkeuring. Of één van de twintig integraties een 502 teruggeeft en je 'm moet retryen met exponentiële backoff, terwijl de rest van de workflow-state overeind blijft. Elk van die dingen is een weekend engineering. Bij elkaar opgeteld zit je op een jaar.
Voor deze herbouw kozen we Temporal. Temporal is een workflow-engine die het langlopende proces als eenheid van code behandelt. Je schrijft een workflow-functie die je leest alsof het synchroon loopt. De engine doet persistentie, retries, timers en signals. Moet je proces drie dagen wachten op een VOG, dan await de workflow-functie gewoon het resultaat. De worker mag crashen, de cluster mag opnieuw worden uitgerold, en als de workflow weer opstart pakt hij precies daar op waar hij was gebleven.
Die ruil betaalt zich terug zodra het proces langlopend is, mensen erbij betrekt en meer dan drie externe systemen raakt. Alle drie waren hier waar.
De worker die het Trello-bord verving
Het hele onboarding-proces leeft nu in één workflow-functie. Die is korter dan de oorspronkelijke Trello-checklist.
import {
proxyActivities,
defineSignal,
setHandler,
condition,
} from '@temporalio/workflow';
import type * as activities from './activities';
const {
requestVOG,
pollVOGStatus,
createPayrollEmployee,
createTimeTrackingAccount,
verifyBankAccount,
sendOnboardingPacket,
scheduleFirstShift,
enqueueForPayrollRun,
} = proxyActivities<typeof activities>({
startToCloseTimeout: '5 minutes',
retry: { maximumAttempts: 5 },
});
export const idDocumentApproved = defineSignal<[boolean]>('idDocumentApproved');
export async function onboardCandidate(contract: SignedContract): Promise<void> {
// Kick off the things that can run without a human, in parallel.
const [vogRef, payrollId] = await Promise.all([
requestVOG(contract.candidate),
createPayrollEmployee(contract),
]);
await verifyBankAccount(contract.candidate.iban);
await createTimeTrackingAccount(payrollId, contract);
// Wait for the recruiter to approve the ID scan, with a 48h timeout.
let approved = false;
setHandler(idDocumentApproved, (ok) => { approved = ok; });
await condition(() => approved, '48 hours');
// The VOG can take days. The activity polls patiently with backoff.
await pollVOGStatus(vogRef);
await sendOnboardingPacket(contract);
await scheduleFirstShift(payrollId, contract.startDate);
await enqueueForPayrollRun(payrollId, contract.payPeriod);
}
Een paar dingen die het aanwijzen waard zijn.
De functie leest van boven naar beneden als een beschrijving van het proces. Geen event bus, geen state-machine diagram aan de muur, geen "wat gebeurt er als de worker tussen stap 14 en stap 15 sterft." Dat hoort bij Temporal.
requestVOG en createPayrollEmployee draaien parallel omdat ze niet van elkaar afhangen. De recruiter ziet die parallelheid niet. Ze ziet alleen dat beide klaar zijn. Scheelt een uur op de mediane wall-clock.
Het idDocumentApproved-signal is het stukje human-in-the-loop. De recruiter bekijkt het gescande ID in ons interne dashboard en klikt op Goedkeuren. Het dashboard verstuurt het signal. De workflow gaat verder. Klikt ze niet binnen 48 uur, dan zakt de workflow door naar een recovery-queue waar iemand achteraan belt.
Activities zijn van nature idempotent. Hier is die voor de payroll-medewerker:
export async function createPayrollEmployee(
contract: SignedContract,
): Promise<string> {
// Loket's API blows up on duplicates. We use a deterministic external id
// built from BSN plus signed-at timestamp, so retries hit the same row.
const externalId = `cand-${contract.candidate.bsn}-${contract.signedAt}`;
const existing = await loket.employees.findByExternalId(externalId);
if (existing) return existing.id;
const created = await loket.employees.create({
externalId,
bsn: contract.candidate.bsn,
firstName: contract.candidate.firstName,
lastName: contract.candidate.lastName,
iban: contract.candidate.iban,
contract: mapContract(contract),
});
return created.id;
}
Doet je activity iets dat de buitenwereld onthoudt (gebruiker aanmaken, mail versturen, betaling inplannen), maak hem idempotent. Temporal retryt voor jou. Hij weet niet dat "verstuur onboarding-mail" niet veilig is om twee keer aan te roepen.
Twintig systemen achter één interface
Vanuit de workflow zien de activity-functies er simpel uit. De implementaties zijn dat niet. Elk van die twintig externe systemen heeft zijn eigen karakter. Loket biedt een nette REST API met voorspelbare foutcodes. Het VOG-portaal heeft helemaal geen echte API; we sturen eHerkenning aan via een headless browser en een serviceaccount. De pensioenuitvoerder mailt een bevestigings-PDF terug, waar een kleine inbox-watcher het polisnummer uit haalt. De API van de tijdregistratieleverancier wil een sessie-cookie die elke zes uur verloopt, dus de adapter draait een kleine login-loop naast de call.
Die rommel hebben we allemaal in de activity-laag geduwd, achter een dunne adapter per systeem. De workflow weet niet dat pollVOGStatus een Puppeteer-sessie inhoudt, of dat createPayrollEmployee retryt door drie foutcodes die Loket alleen in een PDF uit 2019 documenteert. Hij ziet functies die waardes teruggeven. Gaat het VOG-portaal in onderhoud, dan herschrijven we de adapter en blijft de workflow gelijk. Die scheiding is de tweede reden dat Temporal zich terugbetaalt: de workflow is de spec, de activities zijn de smurrie.
De woensdag-14:00-cutoff, opgelost met een timer
De payroll-cutoff was vroeger het ding dat een snelle onboarding alsnog traag maakte. We konden alles op woensdagochtend rond hebben en dan toch de run missen omdat de bank-verificatie-batch nog niet binnen was.
De Temporal-workflow regelt cutoffs expliciet. Zodra alle voorwaarden zijn afgerond, berekent enqueueForPayrollRun het eerstvolgende geldige payroll-moment uit de loonperiode en de cutoff-kalender, en slaapt binnen de workflow tot dat moment voor hij de echte enqueue doet.
Wordt de worker tijdens die slaap opnieuw uitgerold, dan overleeft de timer dat. Loopt de workflow op woensdag 13:55 af, dan vuurt de enqueue om 13:58. Loopt hij om 14:02 af, dan wacht hij tot volgende week woensdag. De recruiter ziet dat in het dashboard zodra de timer staat, dus ze kan de kandidaat waarschuwen voor hij een buskaartje koopt.
Wat er voor het team veranderde
Twee maanden na livegang lag de mediane tijd van contract naar payroll op 31 uur. De p90 op 56 uur. De p95 werd bijna volledig bepaald door de wachttijd op de VOG, en dat is een klus voor het Ministerie van Justitie, niet voor ons.
De recruiters zijn niet ontslagen. Er stond vorige week een hot take op de voorpagina van Hacker News, eentje over hoe CEO's die denken dat AI hun mensen vervangt meestal gewoon slechte CEO's zijn, en de eigenaar van het Den Bossche bureau is niet zo'n CEO. Hij keek naar de vrijgekomen uren en vroeg zijn team wat ze daarmee wilden doen.
Wat ze wilden doen, was meer kandidaten bellen. Ze sloten in het kwartaal na livegang ongeveer 30% meer contracten, met dezelfde headcount en zonder het marketingbudget aan te passen. Het Trello-bord bestaat nog. Het is leeg.
Het punt van een 23-stappenproces automatiseren is niet om de mensen die het draaiden weg te bezuinigen. Het punt is hun de uren teruggeven die ze aan de stappen kwijt waren, zodat ze het werk kunnen doen dat de stappen in de weg zaten.
De nieuwe vorm van de werkdag van de recruiter
Het eerste wat we na livegang wilden weten was hoe het werk eruitzag vanuit de stoel van de recruiter, niet vanuit Grafana. Vier maanden later zaten we bij de recruiter uit de openingsanekdote aan tafel. Haar ochtend begon vroeger met het Trello-bord: openen, scrollen op zoek naar kaarten die ze de dag ervoor had afgevinkt maar vergeten was na te bellen, en een geeltje plakken met de drie of vier dingen waarvan ze zeker wist dat ze ergens iets had gemist.
Nu begint haar ochtend met de stuck list. Dat is de lijst met workflows die op haar wachten: een ID-scan om goed te keuren, een contractwijziging om te bevestigen, een kandidaat die een ondertekende brief nog niet terug heeft gestuurd. De lijst is meestal leeg om 09:30. Ze heeft het Trello-bord in twee maanden niet meer geopend. Het team is ook gestopt met de vrijdagmiddag-Trello-opschoning, vroeger negentig minuten met drie mensen ruziën over de vraag of een vinkje wel of niet aan moest. Die negentig minuten zaten niet in de originele ROI, en het was het eerste dat de office manager noemde toen we vroegen wat er anders was.
Wat we de volgende keer anders zouden doen
Vooral twee dingen.
We hebben het dashboard overdimensioneerd. De eerste versie had een complete Temporal-achtige workflow-viewer voor de recruiters: zichtbare state per activity, retry-tellers, timer-aftellers. Wilden ze niet. Ze wilden één lijst met "wat hangt en wie moet ik bellen." We hebben het dashboard in de tweede sprint zo'n 60% afgeschoren en niemand klaagde.
We hebben de contractwijzigingen te dun gemodelleerd. Ongeveer één op de acht kandidaten verandert iets tussen ondertekenen en starten: uren, uurloon, soms de startdatum. De eerste versie behandelde dat als een nieuwe onboarding, waardoor ze dubbel in Loket terechtkwamen. De huidige versie gebruikt de update-functionaliteit van Temporal om een lopende workflow ter plekke aan te passen. Het schonere patroon, maar we hebben er twee weken op zitten staren voor het klopte.
De audit van vijf minuten die je op je eigen proces los kunt laten
Toen we de onboarding-worker voor het Den Bossche bureau bouwden, liepen we er steeds tegenaan dat "het proces" nergens stond opgeschreven. Het waren drieëntwintig gewoontes in de hoofden van drie mensen. We hebben uiteindelijk een week op hun kantoor gezeten voor we één regel Temporal-code schreven, gewoon kijken. Denk je na over process automation voor je eigen operatie, dan is die observatieweek de goedkoopste week van het hele project.
Voor je ook maar één regel code schrijft, doe dit. Kies één proces in je bedrijf waar een wachtrij voor staat: kandidaat-onboarding, factuurgoedkeuring, retours afhandelen, leveranciers screenen. Klok het deze week vijf keer met een stopwatch. Het gat tussen je snelste en je traagste run is precies waar het werk zit.
Kern
Langlopende processen overleven beter als workflow-functies dan als state machines die je aan elkaar naait met job queues en database-rijen.
FAQ
Waarom Temporal en niet een job queue zoals Sidekiq of Bull?
Job queues pakken één taak per keer. Temporal pakt het hele langlopende proces als één functie. Die ruil betaalt zich terug als werk dagen overspant, op mensen moet wachten en meerdere externe systemen raakt.
Hoe houd je activities idempotent als een extern systeem per call afrekent?
Bouw een deterministisch extern ID per workflow-run en check vóór je aanmaakt. De meeste Nederlandse payroll-leveranciers hebben een findByExternalId. Temporal retryt namens jou, dus de eerste schrijfactie moet veilig herhaalbaar zijn.
Wat is het kleinste proces dat het automatiseren met Temporal waard is?
Loopt een proces langer dan een dag, hangt er minstens één menselijke goedkeuring in en raakt het drie of meer externe systemen, dan betaalt Temporal zich binnen een kwartaal terug. Daaronder volstaat een queue plus een Postgres-tabel meestal prima.
Wat is de VOG en waarom vertraagt hij Nederlandse onboarding?
De VOG is de Verklaring Omtrent het Gedrag, uitgegeven door het Ministerie van Justitie. Digitale aanvragen via eHerkenning duren een paar werkdagen; papieren aanvragen weken. De meeste onboarding-vertragingen zijn ernaar te herleiden.