Email automation
E-mailautomatisering in uitvaartzorg: 980 mails per week
Dinsdagochtend in Almere. 187 ongelezen mails, 41 met een Dela-polisnummer, en een vijftien jaar oud dossiersysteem dat niemand wil migreren. Dit hebben we gebouwd.

Het is dinsdagochtend in Almere. De intake-coördinator van een uitvaartzorggroep met 31 medewerkers opent de gedeelde inbox en ziet 187 ongelezen mails. Eenenveertig daarvan noemen een Dela-polisnummer. Negentien noemen Monuta. Zes komen van een notaris die om dezelfde overlijdensakte vraagt die afgelopen vrijdag al in een thread zat. De rest zijn families die vragen wanneer ze persoonlijke spullen kunnen ophalen, of de rouwkaartproeven klaarstaan, en of opa's pensioen nog een maand doorbetaald wordt.
Die inbox is wat we mochten oplossen.
Het dossiersysteem dat niemand wilde aanraken
De groep draait op een eigen ASP.NET 4.0-dossierapplicatie, in 2011 geschreven door een ontwikkelaar die sindsdien naar Australië is geëmigreerd. Hij werkt. Uitvaartcoördinatoren gebruiken hem dagelijks. Elke uitvaart krijgt een dossiernummer, elke regel hangt aan dat nummer, en elke factuur (staat, verzekeraar, familie) verrekent op dezelfde code.
Vervangen zou betekenen dat je vijftien jaar boekhoudlogica herbouwt op een stack die de schoonbroer van de oprichter niet zou goedkeuren. Dus we hebben hem niet vervangen. We hebben de goedkopere weg gekozen. We hebben eromheen gebouwd.
De randvoorwaarde om te benoemen is de runtime. .NET Framework 4.0 zelf krijgt sinds januari 2016 geen updates meer volgens Microsofts lifecyclebeleid. De applicatie draait nog op Windows Server 2012 R2 in een Hyper-V VM. Hij stelt niets bruikbaars beschikbaar aan de buitenwereld. Geen REST-endpoint, geen webhook, geen message bus. De enige stabiele interfaces zijn de SQL Server 2014-database en een SMTP-relay die al orderbevestigingen verstuurt vanuit het serviceaccount van het systeem zelf.
Dat was genoeg.
Hoe 980 mails per week er echt uitzien
Voor we iets aanraakten, vroegen we tien weken inboxhistorie geëxporteerd naar .eml en getagd door de twee coördinatoren die intake doen. Het kopgetal in de briefing zei 'ongeveer duizend per week'. De echte verdeling zag er zo uit:
- 41 procent waren statusvragen ('wanneer kunnen we langskomen', 'is de kaart goedgekeurd', 'is het notarisstuk binnen')
- 24 procent waren verzekeringsclaims met een Dela- of Monuta-polisnummer erbij
- 18 procent waren documentverzoeken (akte van overlijden, uittreksel, factuur, condoleanceregister)
- 11 procent waren condoleanceberichten van vrienden van de familie aan de overledene, per ongeluk naar de bedrijfsinbox gestuurd
- 6 procent was de rest: leveranciersfacturen, NAW-correcties, af en toe een persvraag
Die verdeling bepaalde het ontwerp. Je bouwt geen enkele agent die al die mails beantwoordt. Je bouwt een classifier die het juiste pad kiest, en je zorgt dat elk pad saai en voorspelbaar is.
Twee wachtrijen en één regel
Het werden twee wachtrijen. Een coördinatorwachtrij, waar altijd een mens reageert voor de familie iets inhoudelijks krijgt. Een autoresponderwachtrij, waar de agent een ontvangstbevestiging stuurt zonder coördinator in de loop.
De regel die ertussen kiest is kort genoeg om uit het hoofd op te zeggen. Als de mail een overlijdensakte, polisnummer, uitkering, notaris, testament, executeur-testamentair noemt, of iets wat de AVG een bijzondere-categorie-gerelateerd besluit zou noemen, gaat hij naar de coördinatorwachtrij. Al het andere mag de agent bevestigen.
We hebben het bewust zo geschreven. De Autoriteit Persoonsgegevens is duidelijk geweest dat automatische besluiten waarbij bijzondere persoonsgegevens betrokken zijn menselijke beoordeling vereisen. Een condoleancebevestiging is geen besluit. Een claimoverdracht wel, ook als de agent hem 'alleen maar routeert'. Die discussie wilden we later niet voeren.
Zodra een automatisch antwoord ook maar iets bevat dat als statusupdate over een claim, uitkering of juridische zaak gelezen kan worden, is het geen ontvangstbevestiging meer. Behandel die grens als dragend.
De classifier
De classifier is een korte prompt tegen een LLM, met een deterministische nacontrole. Het model retourneert een JSON-object met twee velden: category en confidence. Als confidence onder 0.85 ligt, of de voorspelde categorie iets anders is dan het condoleancepad, gaat de mail naar de coördinatorwachtrij met de voorspelde categorie als hint, niet als besluit.
CATEGORIES = [
"status_question",
"insurer_claim_dela",
"insurer_claim_monuta",
"insurer_claim_other",
"document_request",
"sympathy_message",
"vendor_or_admin",
"unclear",
]
def route(email):
pred = classify(email) # returns {category, confidence}
has_policy = POLICY_RE.search(email.body) is not None
if has_policy or pred["category"].startswith("insurer_claim_"):
return Queue.COORDINATOR, "policy_number_detected"
if pred["confidence"] < 0.85:
return Queue.COORDINATOR, "low_confidence"
if pred["category"] == "sympathy_message":
return Queue.AUTORESPONDER, "sympathy_message"
return Queue.COORDINATOR, pred["category"]
De polisnummer-regex is de saaie helft en doet het meeste werk. Dela-polisnummers vallen binnen een bekende prefixreeks, Monuta gebruikt een andere, en beide zijn lang genoeg dat een false positive op een willekeurige negencijferige string zeldzaam is. We loggen elke match met de gematchte substring, zodat een coördinator een gemarkeerde mail in minder dan drie seconden kan verifiëren.
De condoleancebevestiging schrijven
De bevestiging is één alinea. We hebben hem elf keer herschreven voor hij live ging. De eerste versie klonk als een chatbot. De derde klonk als een trouwplanner. De versie die live ging leest alsof de eigenaar van een kleine uitvaartonderneming hem op een rustige ochtend zelf schreef, en dat klopt, want uiteindelijk schreef hij hem zelf. We gaven hem het concept en hij heeft het met een rode pen ingekort.
De bevestiging noemt nooit de naam van de overledene. Hij verwijst nooit naar 'uw verlies' via de relatie. Hij belooft geen terugbelmoment binnen een specifiek tijdvak. Hij zegt: je bericht is binnen, een coördinator is gewaarschuwd, hier is de dossierreferentie als je die al hebt, en hier is het directe telefoonnummer als vandaag te lang is om te wachten.
Hij wordt verstuurd door de bestaande SMTP-relay van het dossiersysteem, vanuit hetzelfde serviceaccount dat al facturatiemails verstuurt. Dat betekent dat SPF, DKIM en DMARC al goed stonden en dat de berichten in de inbox landen die de familie verwacht, niet in spam. Als een legacy systeem al werkende mailauthenticatie heeft, laat dan de uitgaande post van de agent daar doorheen lopen. Een parallel SMTP-pad opzetten is hoe bevestigingsmails bij de helft van de ontvangers in de junkmap belanden.
Aanhaken op het dossiersysteem zonder het aan te raken
We hebben geen enkele regel nieuwe code in de ASP.NET-applicatie geschreven. De integratie is een read-only SQL-view, een write-only tabel en een Windows-service.
De view geeft de zeven velden die de agent nodig heeft: dossiernummer, statuscode, e-mailadres van de primaire contactpersoon, naam van de primaire contactpersoon, verzekeraarscode, polisnummer, timestamp van de laatste gebeurtenis. De agent kan een bestaand dossier opzoeken op e-mailadres of polisnummer, en meer kan hij niet lezen.
De write-only tabel heet inbox_event. De agent voegt een rij toe zodra hij een bericht routeert: dossier id (nullable), email message-id, category, confidence, queue, agent reply id (nullable), timestamp. De dossierapplicatie heeft een kleine uitbreidingspagina, door ons geschreven in plain Razor tegen hetzelfde framework, die inbox_event joint met de bestaande dossierrecords en de coördinator laat zien wat er binnenkwam sinds zij het bestand voor het laatst openden.
De Windows-service polt de IMAP-inbox elke 90 seconden, draait de classifier, kiest de wachtrij, schrijft de rij en geeft (indien van toepassing) het bericht door aan de SMTP-relay. Het zijn tweehonderd regels C# die sinds week drie niet meer zijn aangeraakt.
Wat in de eerste run misging
Drie dingen, alle drie het noemen waard.
Eerste. De agent probeerde aanvankelijk condoleanceberichten van derden te bevestigen (de 11 procent). Dat brak op dag twee, omdat een deel van die berichten van regionale pers waren die om een quote vroeg, en een sjabloon-condoleanceantwoord aan een journalist ziet er pijnlijk uit. We hebben het autoresponderpad versmald tot berichten waarvan het afzenderdomein een freemail-provider is en de body korter dan 600 tekens. De categorie bestaat nog, maar de trigger is strenger.
Tweede. We hadden doorgestuurde mails te licht ingeschat. Een coördinator die een oude thread naar zichzelf doorstuurde om hem 'voor later te parkeren', werd geclassificeerd als verse intake. We hebben een check op de References en In-Reply-To headers toegevoegd, en elk bericht laten vallen dat de dossierreferentie al in het onderwerp had.
Derde. De eerste prompt vroeg het model om emotionele toon te detecteren. De classifier werd er slechter van, niet beter. Toon bepaalt niet de wachtrij. Of er een polisnummer, een notaris of een documentverzoek in staat, dat bepaalt de wachtrij. We hebben het toonveld eruit gehaald en de accuratesse ging omhoog.
Wat de cijfers zeggen na acht weken
De intake-coördinatoren hebben hun tijd-in-inbox voor en na bijgehouden, in hun eigen sheet, omdat we daarom hadden gevraagd. De week voor de agent live ging, waren ze samen 38,5 uur kwijt aan alleen triage, voor er ook maar één regel echt werk gedaan was. Acht weken later staat dat getal op 11,2 uur, en die zitten allemaal in de coördinatorwachtrij, omdat alles in de autoresponderwachtrij al bevestigd is tegen de tijd dat zij het openen.
De agent verwerkt ruwweg 980 berichten per week. Ongeveer 62 procent landt in de coördinatorwachtrij met een categorie-hint, 31 procent wordt bevestigd en geparkeerd, en 7 procent wordt als onduidelijk gemarkeerd en binnen een uur door een mens gelezen. Geen enkel condoleancebericht is naar de verkeerde familie gestuurd. Geen enkele verzekeringsclaim is automatisch bevestigd. Eén persvraag kreeg in week twee een ontvangstbevestiging en een coördinator pakte hem binnen twintig minuten op, precies het soort foutmodus dat we wilden: zichtbaar, snel, te repareren.
Het dossiersysteem is niet aangeraakt.
Wat dit niet is
Dit is geen verhaal over legacy software vervangen door AI. Het is het omgekeerde. Het dossiersysteem blijft. De coördinatoren blijven. De agent doet het deel van het werk dat hen niet nodig heeft, en hij doet het binnen de rails die het bestaande systeem al biedt.
Als je op een vijftien jaar oude line-of-business applicatie zit waar iedereen bang voor is om hem te migreren, is de eerste vraag niet hoe je hem vervangt. Het is waar de inbox staat, en wat er zou gebeuren als de saaie 30 procent ervan zichzelf beantwoordde voor het team inlogt.
Toen we de e-mailagent bouwden voor de Almeerse uitvaartzorggroep, was het ding waar we tegenaan liepen de AVG-grens rond automatische besluiten bij bijzondere persoonsgegevens. We hebben het opgelost door de autoresponder zo klein te maken dat hij er nooit een neemt.
Audit van vijf minuten, mocht je willen: open je gedeelde inbox, exporteer de laatste volle week naar .eml of CSV, en tag elk bericht op wat het vraagt. Als een kwart van het volume dezelfde drie vragen zijn die op dezelfde drie manieren beantwoord worden, weet je al waar je begint.
Kern
Bouw de agent om het legacy systeem heen, niet eroverheen. Het dossier blijft, de coördinatoren blijven, en de agent doet de saaie 30 procent die hen nooit nodig had.
FAQ
Waarom het ASP.NET 4.0-dossiersysteem niet gewoon helemaal vervangen?
Vijftien jaar boekhoudlogica, eigen facturatie en coördinatorgewoontes zitten erop. Een herbouw is een project van meerdere kwartalen. De e-mailagent ging in drie weken live en geeft het team ruimte om een migratie op hun eigen tempo te plannen.
Wat houdt de agent tegen om automatisch op een claim te antwoorden?
Twee checks. Een regex voor de polisnummer-formats van Dela en Monuta, en een LLM-classifier waarvan de confidence-drempel voor het autoresponderpad op 0.85 staat. Alles wat naar een verzekeraar ruikt, gaat naar de coördinatorwachtrij met een categorie-hint.
Vanaf welk adres verstuurt de agent uitgaande mail?
Via de bestaande SMTP-relay van het dossiersysteem, vanuit hetzelfde serviceaccount dat al facturen verstuurt. SPF, DKIM en DMARC stonden al goed, dus bevestigingen landen in de inbox in plaats van de spamfolder.
Hoe worden bijzondere persoonsgegevens onder de AVG afgehandeld?
De autoresponder neemt nooit een besluit over een claim, uitkering of juridische zaak. Hij bevestigt alleen ontvangst. Alles wat een polisnummer, notaris of documentverzoek raakt, gaat naar een mens voor er een antwoord uitgaat.