Automation
Durable factuur-agents: n8n vs Make vs Temporal stack
We rekenden drie durable-execution runtimes door voor een factuur-agent met 4.600 inkoopfacturen per week, met de zevenjarige bewaarplicht erbij. Het goedkoopste antwoord was niet het juiste.

De financial controller van een metaalbewerkingsbedrijf met 20 medewerkers in Apeldoorn opent op een maandagochtend haar gedeelde mailbox en ziet 4.600 inkoopfacturen wachten op haar week. Twee zijn dubbel. Elf staan in CHF. Eén is een creditnota die zich voordoet als factuur. Drieënveertig zijn PDF's die haar huidige OCR direct afwijst. Ze wil een factuur-agent. Die moet lezen, valideren, ontdubbelen, op de juiste grootboekrekening boeken en het resultaat naar Twinfield pushen. Ze wil ook een stap-voor-stap replay van elke beslissing die de agent neemt, zeven jaar bewaard, voor het geval de belastingdienst aanklopt.
Die laatste eis schrapte drie tools van onze shortlist nog voor we één regel productiecode schreven.
Dit is een vergelijking van de drie runtimes die we voor die build hebben doorgerekend: n8n self-hosted, Make.com op een Teams-plan, en een handgemaakte Temporal + TypeScript-stack. De criteria waren smal en onverbiddelijk — kosten per workflow bij 20.000 facturen per maand, een replay-bestendige stappenhistorie voor de zevenjarige bewaarplicht, en wie de pager krijgt als Twinfield zijn OAuth refresh-token op zondagnacht om 02:00 roteert.
Wat de agent feitelijk doet
Voor we het over runtimes hebben: eerst het werk. Eén factuur van begin tot eind:
- Ophalen uit IMAP of de UBL-inbox van Twinfield.
- Classificeren (factuur / creditnota / aanmaning / spam).
- OCR plus gestructureerde extractie met een vision-model, fallback naar een rule extractor voor bekende leveranciers.
- Ontdubbelen tegen een 90-daags Postgres-venster op (supplier, invoice_number).
- BTW-regels valideren, vreemde valuta omrekenen op factuurdatum, BTW-code, grootboek mapping.
- Pushen naar Twinfield via de SOAP API, document-GUID vastleggen.
- Als er goedkeuring nodig is (boven €2.500 of een nieuwe leverancier), routeren naar Slack met een Approve / Reject-kaart.
- De volledige beslissingsketen bewaren.
Gemiddeld elf afzonderlijke stappen per factuur, met twee externe API's die kunnen falen (Twinfield, de LLM), één human-in-the-loop-tak, en een harde eis dat er tussen stappen niets verloren gaat. De runtime-laag heeft als taak die pipeline durable te houden door crashes, restarts, deploys en rate-limit-stormen heen.
n8n self-hosted: goedkoop tot het dat niet meer is
Hier zijn we begonnen, omdat de meeste teams hier beginnen. n8n is een node-based workflow engine — self-host 'm op een VPS van €40/mnd, sleep je IMAP-node op het canvas, verbind 'm met een LLM-node, dan een Postgres-node, dan een HTTP-node voor Twinfield.
Voor het happy path werkt het. We hadden binnen twee dagen een proof-of-concept dat in één middag 200 facturen verwerkte.
Het gedoe begon op drie plekken.
Bewaartermijn van execution data. n8n schrijft elke execution naar zijn eigen database. Je kunt pruning en een leeftijdscap configureren (zie de docs), maar als je bewaarplicht zeven jaar is, kun je niet prunen. Bij 20.000 facturen × 11 nodes per execution × tientallen kilobytes node-IO per stuk schrijf je ruwweg 11 GB per maand aan execution data die je nooit mag verwijderen. Na jaar zeven zit je op bijna een terabyte operationele database, en je Postgres-backups zijn nu de bottleneck bij elke deploy.
Replay is niet deterministisch. Als de belastingdienst vraagt waarom factuur 18293/A in maart 2027 op grootboek 7000 is geboekt, kun je die node niet los opnieuw draaien. n8n bewaart de inputs en outputs van elke node, maar de code die de replay uitvoert is de code die vandaag op disk staat. Heb je de workflow tussendoor geüpdatet, dan is wat je replayt niet wat er liep. Je kunt de JSON van de workflow op het moment van execution als side-effect opslaan, maar dat moet je zelf bouwen, plus de loader die 'm reconstrueert.
De pager om 02:00. De ingebouwde retry van n8n werkt per node. Roteert Twinfield zijn OAuth refresh-token op zondagnacht om 02:00, dan valt elke lopende execution om op de Twinfield-node. n8n probeert het drie keer, faalt, en de workflow eindigt in error. Er is geen globale primitive "pauzeer deze tak en hervat zodra het token weer werkt". Iemand moet inloggen in de editor, gefaalde executions één voor één replayen, en hopen dat er geen factuur is verdwenen tussen IMAP delete-on-read en de gefaalde schrijfactie.
De kosten per workflow waren laag: zo'n €0,004 per factuur inclusief de VPS en de Postgres-tier. De kosten van de oproepdienst waren dat niet. Zondagochtend hoorde nu bij het takenpakket.
Make.com: het operation-tellerprobleem
Make verkoopt betrouwbaarheid als feature, en voor kleine flows levert het dat. Het model verschilt van n8n: elke module binnen een scenario kost je één operation, en operations koop je in maandbundels.
We hebben dezelfde pipeline van elf stappen gemodelleerd. Bij 20.000 facturen per maand kom je op ruwweg 220.000 operations baseline, plus retries, plus de Slack-goedkeuringstak op de ~15% facturen die daar belanden, plus de dedup-lookup. In de praktijk zit je op zo'n 320.000 operations per maand in steady state, met pieken erboven in factuur-intensieve weken.
Om 350.000 operations te dekken op een Teams-plan keken we naar de hogere tier met een kostprijs rond €0,03 per factuur — bijna tien keer het n8n-getal. De prijslijst van Make beloont kleine scenario's en straft hoog volume af.
Het verhaal rond bewaartermijnen lijkt op n8n: execution logs blijven binnen het plan-venster (30 tot 90 dagen op Teams), en zevenjarige verdedigbaarheid is geen onderdeel van het product. Je kunt exporteren, maar dan exporteer je JSON naar je eigen bucket, en dan had je net zo goed de agent kunnen bouwen op een runtime die je zelf in beheer hebt.
Het 02:00-probleem wordt deels opgelost door Make's incremental retry en queue, maar hetzelfde gat zit erin: heeft de OAuth refresh-rotatie eenmalig een interactieve stap nodig (bij Twinfield gebeurt dat soms, voor nieuwe scopes), dan hangt het scenario in plaats van te falen, en Make pagert niemand — want Make weet niet dat juist deze HTTP 401 bijzonder is.
Koop je een hosted workflow runtime om aan de zevenjarige Nederlandse bewaarplicht te voldoen, lees dan de SLA over dataretentie van de vendor voordat je tekent. De meeste cappen operationele logs op 30 tot 90 dagen. De retentie die je daadwerkelijk nodig hebt, moet in jouw warehouse staan, niet in dat van hen.
Temporal + TypeScript: de runtime die ook het archief is
Temporal is een ander type tool. Geen no-code canvas. Het is een durable-execution engine die jouw TypeScript-code (of Go, Java, Python) draait en elke stap vastlegt in zijn event history. Event history is de centrale abstractie: elke input, elke output, elke retry, elk signaal, elke timer. De workflow-code is deterministisch, en de event history is voldoende om de hele execution byte voor byte te replayen tegen dezelfde codeversie die origineel draaide.
De runtime geeft je het bewaarplicht-artefact gratis. Pin de codeversie van de workflow in het event, bewaar de event history zeven jaar (het is een platte protobuf-log die goed comprimeert — we maten ruim 1,4 KB per factuur na gzip), en de vraag van de belastingdienst wordt een replay-commando.
import { Worker, NativeConnection } from '@temporalio/worker'
import * as activities from './activities'
const worker = await Worker.create({
connection: await NativeConnection.connect({ address: 'temporal:7233' }),
namespace: 'factuur',
taskQueue: 'invoices',
workflowsPath: require.resolve('./workflows'),
activities,
})
await worker.run()
De workflow zelf leest bijna als de prozaversie van de pipeline.
import { proxyActivities, condition, setHandler } from '@temporalio/workflow'
import { approvalSignal } from './signals'
import type * as acts from './activities'
const a = proxyActivities<typeof acts>({
startToCloseTimeout: '2 minutes',
retry: { maximumAttempts: 5, initialInterval: '4s' },
})
export async function processInvoice(raw: RawInvoice) {
let decision: 'approve' | 'reject' | null = null
setHandler(approvalSignal, (d) => { decision = d })
const cls = await a.classify(raw)
if (cls.type !== 'invoice') return a.archive(raw, cls.type)
const extracted = await a.extract(raw)
if (await a.isDuplicate(extracted)) return a.archive(raw, 'duplicate')
const coded = await a.codeToGrootboek(extracted)
if (coded.requiresApproval) {
await a.requestSlackApproval(coded)
await condition(() => decision !== null, '7 days')
if (decision === 'reject') return a.archive(raw, 'rejected')
}
return a.pushToTwinfield(coded)
}
Het OAuth refresh-token-probleem om 02:00 wordt aangenaam. De Twinfield-activity vangt de 401, gooit een non-retryable application error met tag TWINFIELD_AUTH_EXPIRED, en een aparte workflow — een langlopende tokenSentinel — wordt wakker, voert de refresh uit en seint elke gepauzeerde factuur-workflow om opnieuw te proberen. Niemand wordt gepaged. Facturen die om 02:00 onderweg waren, ronden zichzelf om 02:04 af.
Kosten. Self-hosted Temporal op een drie-node Postgres-backed cluster kost ons op deze schaal rond €180/mnd aan infrastructuur. Temporal Cloud, dat we de eerste zes maanden gebruikten terwijl we het cluster hardden, rekent per Action af; hun prijslijst kwam bij 20.000 facturen per maand uit op onder de $40 inclusief een kleine basis-fee. Kosten per workflow: €0,0075 op eigen infra, €0,0018 op Cloud — goedkoper dan Make, in absolute zin iets duurder dan n8n, beide met het zevenjarige archief ingebakken in de runtime in plaats van eromheen gebouwd.
Kosten per workflow, naast elkaar
Bij 20.000 facturen per maand, all-in:
- n8n self-hosted: ~€80/mnd runtime, ~€0,004 per factuur, met een onopgelost zevenjaars-retentieprobleem en een pager op zondagochtend.
- Make Teams (hogere tier): ~€600/mnd runtime, ~€0,03 per factuur, met 90 dagen log-retentie die je elders moet spiegelen.
- Temporal self-hosted: ~€180/mnd runtime, ~€0,0075 per factuur, zevenjarige event history als first-class feature.
- Temporal Cloud: ~€35/mnd runtime, ~€0,0018 per factuur, dezelfde archiefgaranties, geen cluster om te beheren.
Het laagste cijfer per factuur is n8n. De laagste total cost of ownership, zodra je de oproepdienst-uren en het bouwwerk rond retentie meerekent, is Temporal. We hebben die spreadsheet op drie manieren voor drie verschillende klanten gedraaid en het antwoord komt steeds hetzelfde uit zodra de bewaarplicht in beeld komt.
Wie patcht de workflow om 02:00
Dit is de onsexy vraag die de build bepaalt.
Op n8n is het antwoord: jij. De editor is de enige plek waar je een gefaalde execution kunt replayen, en er is geen programmatic manier om "Twinfields refresh-token is geroteerd, pauzeer alles wat daarvan afhangt" te detecteren. Dat schrijf je als losse cron, en als die misgaat, is de workflow-editor je incident-tool. We hebben dat drie weken volgehouden en hebben veertien developer-uren weekend-oproepdienst verstookt voor we wegging.
Op Make is het antwoord: jij, plus de statuspagina van Make. Het 02:00-probleem uit zich als een hangend scenario, niet als een gefaald — incremental retry blijft proberen. Finance ziet het maandagochtend als ze niet kunnen aansluiten.
Op Temporal is het antwoord: niemand, meestal. De tokenSentinel-workflow is zelf durable, draait elk kwartier, beheert de refresh, en seint de factuur-workflows om door te gaan. De enige keer dat we in negen maanden werking gepaged zijn, was toen Twinfield de vorm van de refresh-response veranderde — een probleem op codeniveau, geen runtime-probleem, en Temporal had elke falende workflow netjes gepauzeerd in afwachting van onze fix.
Wat we hebben opgeleverd
We hebben dit de eerste zes maanden op Temporal Cloud gebouwd en zijn naar self-hosted Temporal op het Hetzner-cluster van de klant gemigreerd toen het volume stabiel was. De factuur-agent verwerkt nu tussen 4.200 en 5.100 facturen per week, met een straight-through rate van 96,4% en een mediane doorlooptijd tot Twinfield van 1,8 dag. Het zevenjarige archief is één S3-bucket met gzipte protobuf, afgerekend in centen per maand.
Toen we deze AI-agents-stack voor de fabriek in Apeldoorn bouwden, bleek de keuze voor de durable-execution-laag meer uit te maken dan de keuze voor de LLM. We hebben de prompt twee keer herschreven, de runtime nooit.
Scope je een vergelijkbare build? Dit is de audit van vijf minuten. Lijst de stappen in je pipeline op. Vermenigvuldig met je maandvolume. Stel je kandidaat-runtime dan twee vragen: hoe lang bewaart hij de execution trace, en is de code die liep nog de code die je opnieuw kunt draaien. Is één van beide antwoorden onbevredigend, stop met rekenen en kies een ander tool.
Kern
De goedkoopste runtime per factuur is zelden de goedkoopste totaalkost zodra je zevenjarige retentie en de oproepdienst-uren rond OAuth-rotaties meerekent.
FAQ
Is Temporal overkill voor een bedrijf van 20 man?
Voor één workflow met laag volume, ja. Voor één met zevenjarige audit-retentie of externe API's die credentials roteren, verdient de runtime zichzelf binnen een kwartaal terug op alleen al de bespaarde oproepdienst-uren.
Voldoet n8n in zijn eentje aan de Nederlandse bewaarplicht?
Niet rechtstreeks. De execution database van n8n is operationeel, geen archief. Je moet execution data zelf naar lange-termijn-opslag spiegelen en workflows zelf version-pinnen voordat een belastingcontrole landt.
Waarom geen Zapier voor deze workload?
Bij 20.000 facturen per maand komt de per-task-prijs van Zapier uit op een veelvoud van het Make-getal, en dezelfde gaten in retentie en OAuth-rotatie gelden. De randvoorwaarden die Make uitsloten, sluiten ook Zapier uit.
Wat kostte Temporal Cloud op deze schaal?
Onder de $40 per maand bij 20.000 facturen, afgerekend per Action. Self-hosted op Hetzner kwam uit op rond €180 per maand inclusief de Postgres-tier, monitoring en back-ups.
Moet je TypeScript schrijven om Temporal te gebruiken?
Nee. Temporal ondersteunt naast TypeScript ook SDK's voor Go, Java, Python, .NET en PHP. Wij kozen TypeScript omdat de rest van de agent-stack daar al in geschreven was.