Email automation
Email-agent op een 13 jaar oud ERP: 740 offertes in Enschede
Op een dinsdagochtend in maart stonden er 40 mails in de inbox voor de eerste koffie. Nu heeft een agent ze al gelezen, geprijsd en geparkeerd.

De inbox van 8:47
Op een doordeweekse dinsdag in maart opende de salesdesk bij een textielfabrikant in Enschede (27 medewerkers) Outlook op 41 ongelezen mails. Drieëntwintig waren offerteaanvragen, meestal in Duits en Nederlands, soms met een PDF van een onderdeeltekening of een tech-sheet van een hittebestendige coating. Om 9:15 hadden de twee accountmanagers de makkelijke eruit gepikt. Om 11:30 lagen de lastigere threads op de stapel 'moet ERP-check'. De eerste echte offerte ging om 11:40 de deur uit. De laatste van de ochtendbatch volgde tegen lunchtijd.
Dat was de uitgangssituatie. Twee uur en veertig minuten tussen de eerste mail en de eerste offerte de deur uit, gemiddeld over een hele maand inkomende offerteaanvragen. De email-agent die we in februari uitrolden bracht dezelfde ochtendbatch van 2u40 terug naar 18 minuten. In de drie maanden dat we hem na go-live bijhielden miste hij geen enkele keer een margedrempel-override.
Dit is het verhaal hoe dat werkte, en waar het bijna mis ging.
Waarom de inbox er zo uitzag
Het bedrijf maakt technisch textiel (zwaar industrieel bandwerk, transportbandvoeringen, slijtmouwen) in batches van 20 tot 600 meter voor kopers in de DACH-regio en de Benelux. Een typische offerteaanvraag heeft zes variabelen: breedte, lengte, coatingspec, snijpatroon, levertijd en incoterm. De koper stuurt zelden alle zes. De meeste threads beginnen met twee van de zes en een vage verwijzing naar 'hetzelfde als vorige keer' of 'de spec van de Krefeld-order'.
Het echte werk van de salesdesk was nooit het uittikken van de offerte. Het was het reconstrueren van de spec uit een onvolledige mail, plus de klantgeschiedenis, plus een blik in Ridder iQ om de laatste levering aan dat account op te zoeken. Ridder iQ is een Nederlands ERP dat sinds het begin van de jaren tien de ruggengraat vormt van middelgrote maakbedrijven in de Benelux. Degelijk product. De omgeving die we aantroffen draaide op een schema uit 2013 met vijf lagen custom fields op de artikelmaster en een SOAP-connector waar sinds 2018 niemand meer aan had gezeten.
De volumes: 740 offertethreads per week over zes mailboxen. Ruwweg 60% terugkerende kopers, 30% varianten op eerdere orders en 10% écht nieuw specwerk dat een engineer nodig had.
Wat de agent feitelijk doet
De pipeline is weinig opwindend. Een binnenkomende offerteaanvraag landt in een gedeelde Microsoft 365-mailbox. Een Graph subscription stuurt de change notification naar een queue. De agent doet vijf dingen op rij:
- Leest de thread en bijgevoegde PDF's, haalt de zes-variabelen-spec eruit en vult de gaten waar mogelijk ('hetzelfde als vorige keer' wordt gematcht tegen de orderhistorie van de klant).
- Roept het Ridder iQ SOAP-endpoint aan om de artikelmaster-records op te halen die bij de spec passen, met huidige voorraad en standaardkostprijs.
- Berekent een conceptofferte aan de hand van de prijsregels voor dat klantsegment, de actuele FX-koers voor niet-EUR threads, en de margedrempel voor die materiaalklasse.
- Schrijft een conceptantwoord in de oorspronkelijke thread, in de oorspronkelijke taal, met de offerte in de mail en de aannames expliciet erbij.
- Zet de thread in een map 'goedkeuring nodig' als één van de vorige vijf stappen onder de confidence-drempel zat, of in 'klaar om te versturen' als alles erdoor kwam.
Een accountmanager werkt de goedkeuringsmap als eerste af. De klaar-om-te-versturen map wordt in batch nagelopen en met één toetsaanslag per thread vrijgegeven.
Ridder iQ en de SOAP-connector die het bijna sloopte
De integratie was het lastige deel. Ridder iQ heeft een SOAP-API die, mild gezegd, van zijn tijd is. De meeste velden die je voor een prijsberekening écht nodig hebt zitten niet in de standaard-WSDL: de custom marge-overrides, de klantspecifieke kortingstrap, de vlag 'niet offreren onder X'. Die staan in extension tables die de implementatiepartner in 2017 heeft toegevoegd, elk met zijn eigen naamgevingsconventie.
We hebben uiteindelijk een dunne Node-service geschreven die de SOAP-client inpakt en een schone REST-laag biedt voor de agent. Ongeveer 600 regels TypeScript. Het meeste is veldnaam-vertaling.
// abn-ridder-bridge/src/articles.ts
import { createClientAsync } from "soap";
import { z } from "zod";
const ArticleSpec = z.object({
articleCode: z.string(),
customerCode: z.string().optional(),
width_mm: z.number(),
length_m: z.number(),
coating: z.string(),
});
export async function fetchArticleWithOverrides(
spec: z.infer<typeof ArticleSpec>,
) {
const client = await createClientAsync(process.env.RIDDER_WSDL!);
const [base] = await client.GetArticleAsync({
code: spec.articleCode,
});
const [overrides] = await client.GetCustomerOverridesAsync({
customer: spec.customerCode ?? "",
article: spec.articleCode,
});
return {
standardCost: base.StdKostprijs, // 2013 Dutch field name
marginFloorPct: overrides.MinMargePct, // extension table
discountLadder: overrides.KortingTrap, // extension table
listPrice: base.Verkoopprijs,
};
}
De veldnaam-vertaling alleen al kostte twee weken. Het 2013-schema gebruikt Nederlandse afkortingen op de ene plek en Engels op de andere. De 2017-extension tables vormen hun eigen dialect. De schone REST-laag was niet onderhandelbaar: de agent heeft niets te zoeken in wat StdKostprijs betekent.
Bouw je een agent boven op een verouderd ERP, begin dan met de adapter en behandel hem als een echt product. De agent krijgt elk half jaar een modelupgrade. De adapter draagt het systeem tien jaar lang.
De margedrempel-override
Waar de salesdesk het meest om gaf was de override-logica. Elke materiaalklasse heeft een margedrempel: het percentage waaronder een offerte expliciete goedkeuring vereist, ongeacht wat de klantgeschiedenis suggereert. Een offerte die zonder goedkeuring onder de drempel zakt is precies het soort fout dat bij een order van 600 meter een hele maand winst op die productlijn kan wegvegen.
De naïeve implementatie, 'als berekende marge < drempel, naar mens', was niet genoeg. De regel die de desk daadwerkelijk hanteerde was gelaagd: de drempel verschilt per klantsegment, per grondstof-lot (sommige lots hebben promotionele pricing die de leverancier kwijt wil), en er zijn twee benoemde accounts met een contractueel vastgelegde drempel die boven het segmentstandaard gaat.
We hebben de volledige trap vastgelegd in één beslissingsfunctie. Die geeft één van vier statussen terug: auto_send, review_quick, review_engineer of block_with_reason. De agent mag niets versturen dat niet auto_send is. Voor de andere drie mag hij wél een concept schrijven.
Waarom dit nu meer dan ooit telt: op engineeringsforums duikt steeds hetzelfde patroon op, namelijk agents die acties uitvoeren die hun operators niet hadden geautoriseerd en die niet makkelijk terug te draaien zijn. De les die Anthropic in zijn eigen building-effective-agents-stuk blijft herhalen is dezelfde: hou de zware beslissingen in deterministische code, en zet het model in waar de ambiguïteit het echte probleem is. Een antwoord op een offerteaanvraag is een offerte. Een offerte op papier is in de meeste B2B-contexten in de EU bindend genoeg om na te komen. Voor elke cent onder de drempel moet de agent fout zitten richting 'ik heb een concept gemaakt, jij drukt op verzenden'.
Wat 18 minuten echt betekent
Van 2u40 naar 18 minuten is de kop, maar het is eerlijker om te vertellen wat daaronder zit.
Die 18 minuten is de tijd tussen binnenkomst en verzending, voor de threads die de agent als auto_send classificeerde. Dat is ruwweg 62% van de ochtendbatch. Nog eens 28% komt in review_quick terecht en gaat binnen een uur de deur uit, omdat de accountmanager alleen de aannames hoeft te checken en op verzenden hoeft te drukken. De overige 10%, de review_engineer- en block_with_reason-threads, kosten nog steeds een halve ochtend. Dat zal altijd zo blijven.
Eerlijke framing: de agent maakte niet de hele salesdesk over de hele linie sneller. Hij maakte de saaie tweederde van de inbox vrijwel instant, gaf de accountmanagers de ruimte om hun ochtend aan de engineer-threads te besteden, en bracht de gemiddelde wachttijd voor de klant van 'voor de lunch' terug naar 'voor de tweede espresso'. De winst is niet 'AI vervangt de salesdesk'. De winst is dat de salesdesk zijn ochtenden besteedt aan de 10% threads die echt een mens nodig hebben, in plaats van de 90% die dat niet doet.
Drie dingen die in de eerste maand stuk gingen
De PDF-parser was de eerste. Kopers sturen tech-sheets in alle vormen, van schone vector-PDF's tot telefoonfoto's van een afdruk. De spec-extractie van de agent klopte voor 94% bij vector-PDF's en voor 71% bij gescande. We hebben een confidence-score per veld toegevoegd en elke offerte met een gescande PDF als input gedegradeerd naar review_quick, ongeacht wat de marge zei. Valse precisie is erger dan helemaal geen offerte.
De 'hetzelfde als vorige keer'-lookup was de tweede. In de eerste week matchte de agent op klant plus artikelcode en pakte ruwweg 4% van de tijd de verkeerde order. Klanten hergebruiken dezelfde artikelcode voor verschillende sneden. We hebben de lookup herschreven zodat hij klant plus artikelcode plus minstens één matchende variabele uit de nieuwe aanvraag (breedte, lengte of coating) vereist. De 4% zakte naar 0,3%, en die werden allemaal opgevangen door de handmatige review_quick-check.
De Duitse aanhef was de derde. In week één antwoordde de agent kopers waar het bedrijf al jaren mee werkte met 'Sehr geehrte Damen und Herren'. De salesdesk leest dat als kil. We hebben een per-contact aanhef-cache gebouwd uit de verzonden mail van de afgelopen 18 maanden, met een fallback naar de formele standaard alleen voor onbekende contacten. Klein dingetje. Klanten hadden het meteen door.
Wat er niet veranderde
Het prijsmodel. De klantrelaties. De twee accountmanagers. De engineer. De Ridder iQ-omgeving. De mappenstructuur in Outlook. De sales-standup op dinsdagochtend, die nog steeds om 9:00 begint omdat de routine de routine is.
Wat wél veranderde: de standup gaat niet meer over het leegmaken van de inbox. Hij gaat over de threads in review_engineer. Dat is een beter gesprek om te voeren.
Hoe je hier over nadenkt voor je eigen ERP
Een korte versie, voor de lezer met een vergelijkbaar probleem die er niemand voor in gaat huren.
Bouw eerst de adapter. Op welk ERP je ook zit (Ridder iQ, Exact, Unit4, AFAS, SAP Business One, dat zelfgebouwde PHP-ding uit 2011), de connector is het dragende deel. De agent erbovenop is vervangbaar. De schone REST-laag vóór je ERP is wat je in 2030 nog steeds gebruikt.
Leg de override-logica vast als een beslissingsfunctie, niet als prompt-regel. Als alleen de agent de margedrempel kent, kun je hem niet testen, niet auditen, en niet van model wisselen zonder alles opnieuw te valideren. Een pure functie met unit tests staat tussen jou en een dure fout.
Standaard: concept. De agent verstuurt niets waar hij niet zeker genoeg over is, gemeten aan de drempel die de klant verwacht. Al het andere is een concept dat een mens vrijgeeft. Zodra je die drempel laat zakken, gok je met offertes op papier.
En meet het juiste. 'Tijd bespaard' is de verkeerde metriek voor een salesdesk, want accountmanagers vullen de vrijgekomen uren met wat er ook maar de inbox in komt. De juiste metriek is de wachttijd voor de klant tussen ontvangen aanvraag en verzonden offerte. Dat getal is zichtbaar voor de koper, en daar shopt hij feitelijk op.
Eén klein ding om vandaag te doen
Werk je met een offerte-intensieve inbox, neem dan vanmiddag twintig minuten om twee getallen te tellen: hoeveel offerteaanvragen er vorige week binnenkwamen, en welk percentage daarvan varianten waren op een offerte die je al eens had verstuurd. Die ratio is de bovengrens van wat een email-agent van je bord af kan halen. Boven de 50% staat de rekensom waarschijnlijk al in je voordeel. Toen wij de email-agent voor de Enschedese fabriek bouwden, was de ratio 88%. We zijn diezelfde week aan de adapter begonnen.
Kern
Bouw eerst de adapter, leg de zware regels vast als deterministische code, en laat de agent alles concepten waar hij niet zeker over is.
FAQ
Verstuurt de agent ooit zelf een offerte?
Alleen als de beslissingsfunctie auto_send teruggeeft. Dat vereist hoge confidence op elk veld én een berekende marge ruim boven de drempel. De rest blijft concept en wacht op een mens.
Hoe lang duurde de integratie met Ridder iQ?
Ongeveer zes weken in totaal. Twee weken gingen op aan veldnaam-vertaling tegen het 2013-schema en de 2017-extension tables. De agentlaag erbovenop kostte ruwweg tien dagen.
Welk model draait de spec-extractie?
Een frontier-model met vision voor de PDF-stap, en een kleiner model voor het opschonen van de gestructureerde velden. Die keuze zit verstopt achter de adapter, zodat je hem kunt wisselen zonder ERP-code aan te raken.
Wat gebeurt er als het model de offerte verkeerd inschat?
Dat blijkt vrijwel altijd uit een lage confidence-score op één van de zes specvelden, waarna de thread naar review_quick gaat. De block_with_reason-status vangt de rest af voordat er iets de deur uit gaat.
Werkt dit ook voor een kleiner bedrijf met een eenvoudiger ERP?
Ja, en meestal makkelijker. De bottleneck in dit project was het verouderde SOAP-vlak. Een REST-native ERP halveert het adapterwerk.