Email automation
Zalando RMA-reconciliatie: hoe een email-agent €18k redt
Elke vrijdagmiddag schreef de financieel verantwoordelijke bij een Tilburgse modegroothandel Zalando-creditnota's af die ze geen tijd had te betwisten. Wij bouwden haar een email-agent.

Het is 16:40 op een vrijdag. De financieel verantwoordelijke bij een modegroothandel met 36 mensen in Tilburg heeft 47 openstaande Zalando-creditnota's verdeeld over twee browsertabs, een Picqer-retourrapport op haar tweede scherm, en om 17:15 moet ze haar kinderen ophalen van school. De creditnota's komen samen op €4.200 aan chargebacks. Sommige zien er fout uit. Ze accepteert ze allemaal, klapt de laptop dicht en loopt naar buiten.
Vermenigvuldig die vrijdag met 50 weken en je hebt de orde van grootte die we aantroffen toen het bedrijf ons belde. De financieel verantwoordelijke was niet lui. Ze was rationeel. Er bestond geen vorm van werk waarin 1.800 wekelijkse RMA's in de agenda van één persoon pasten, dus de rationele keuze was om het verlies te slikken en volgende week weer orders te versturen.
Dit is het verhaal van de email-agent die we bouwden om die reconciliatie te doen, wat hij ving, en de stukken die ons onderweg verrasten.
De vorm van een Zalando-RMA-email
Iedere werkdag krijgt de returns@-mailbox van de klant een stapel geautomatiseerde Zalando-berichten binnen. Elk bericht staat voor een door de klant gestarte retour die nu het magazijn van Zalando heeft bereikt, is geïnspecteerd, en aan de leverancier is teruggefactureerd. Een typisch bericht heeft een korte body, een CSV als bijlage, en een PDF-creditnota. De CSV is de bron van waarheid die Zalando wil dat je gebruikt: er staan een RMA-referentie in, het ordernummer, de artikelen die de klant heeft teruggestuurd, een reden-code (defect, past niet, bedenktijd, enzovoort), en een refundbedrag per regel.
De adder onder het gras is dat de CSV de Zalando-administratie is van wat er terug is gekomen. Het is geen scan van wat er daadwerkelijk in hun magazijn is gearriveerd. Het is ook geen controle tegenover wat jouw magazijn überhaupt heeft verzonden. Dat zijn twee aparte joins, en ze leven in twee aparte systemen. Wie de reconciliatie doet, is eigenaar van beide joins.
Wat Picqer weet wat Zalando niet weet
De klant draait zijn magazijn op Picqer, een Nederlands fulfillment-platform dat veelvoorkomend is bij kleine en middelgrote e-commerce in de Benelux. Picqer houdt de uitgaande kant bij (wat je hebt verzonden, wanneer, met welke EAN) en, nuttiger in dit geval, de retourkant: wat fysiek binnenkwam, gescand door je team, op staat beoordeeld, en gefotografeerd als de staat iets anders is dan nieuw met labels.
Dat betekent dat Picqer vragen kan beantwoorden die een Zalando-creditnota niet kan:
- Heeft de klant drie artikelen geretourneerd, of slechts twee?
- Is de SKU op de creditnota daadwerkelijk een SKU die we op die order hebben verzonden?
- Toen de creditnota 'defect' zei, beoordeelde onze retourbalie het artikel toen als defect, of als nieuw met labels?
Kun je die drie vragen beantwoorden voordat de creditnota automatisch betaald wordt, dan kun je de foute betwisten. Kun je dat niet, dan schrijf je het verschil af. Dat was de status quo bij deze klant tweeënhalf jaar lang.
De join die niemand met de hand wil doen
De rekensom is hard. 1.800 RMA's per week is 360 per dag. Tegen 90 seconden per reconciliatie (CSV openen, order zoeken in Picqer, regels nalopen, beslissen) is dat negen uur financetijd per dag. Het bedrijf heeft één financieel verantwoordelijke. Dus het werk wordt niet gedaan, de creditnota's worden geaccepteerd, en het verlies wordt onder 'kanaalwrijving' geboekt in de winst-en-verliesrekening.
De kosten van dat verlies werden pas zichtbaar toen we de audit deden. Over zes weken historische data waren foutief gelabelde creditnota's goed voor €18.400 aan afschrijvingen per maand. Dat zijn geen 1.800 disputes per maand. Dat is de schijf waar Picqer en Zalando zo ver uit elkaar lagen dat het de moeite waard was om te betwisten.
De audit zelf kostte drie dagen om CSV's tegen Picqer-exports te leggen in een spreadsheet. Dat is precies het werk dat we de agent wilden laten doen, en het één keer met de hand doen was de enige manier om te leren welke vormen van mismatch vaak voorkwamen, welke duur waren, en welke een model kon worden vertrouwd om te markeren. We zijn elk agent-project sindsdien zo begonnen. De kortste weg naar een nuttige agent is een week lang zelf de agent zijn.
Architectuur
De agent heeft vier taken: de Zalando-emails ophalen, gestructureerde data eruit halen, de bijbehorende record uit Picqer halen, en een conceptbetwisting opstellen als de twee niet kloppen. Niets ervan is exotisch. De waarde zit in het aan elkaar knopen van de stappen en dat draaien op elk inkomend bericht in plaats van elke vrijdagmiddag.
De mailbox lezen we over IMAP. Als je tussen versies kiest, is RFC 9051 de actuele IMAP4rev2-spec en die richten we aan voor nieuwe builds. De extractiestap is één modelcall die de email-body en de bijgevoegde CSV neemt en een getypte record teruggeeft met de RMA-referentie, het order-ID, de regels en het totale credit-bedrag. De Picqer-join is één API-call.
Voor het extractiemodel maakt de keuze minder uit dan mensen verwachten. We gebruiken een klein algemeen model en sturen alleen echt dubbelzinnige bijlagen door naar iets groters. Bij 1.800 berichten per week domineert de prijs per call de totale rekening, dus we tunen op kosten vóór accuratesse en laten de reconciler stroomafwaarts het echte oordeel vellen. De reconciler is gewone code, geen LLM, omdat gewone code is wat je wilt als de vraag 'klopt dit getal met dat getal' is.
async function fetchPicqerReturn(rmaReference) {
const url = `https://${SUBDOMAIN}.picqer.com/api/v1/returns` +
`?reference=${encodeURIComponent(rmaReference)}`;
const res = await fetch(url, {
headers: { Authorization: `Basic ${PICQER_KEY}` },
});
if (!res.ok) throw new Error(`Picqer ${res.status}`);
const hits = await res.json();
return hits[0] ?? null;
}
De reconciler is klein. Hij loopt door de Zalando-regels heen, vindt de bijbehorende Picqer-scan op productcode, en noteert wat afwijkt.
function reconcile(zalando, picqer) {
if (!picqer) {
return { verdict: 'dispute', reason: 'no_return_received' };
}
const issues = [];
for (const line of zalando.lines) {
const scan = picqer.products.find(p => p.productcode === line.sku);
if (!scan) {
issues.push({ type: 'sku_not_received', sku: line.sku });
continue;
}
if (scan.amount < line.quantity) {
issues.push({
type: 'quantity_mismatch',
sku: line.sku,
billed: line.quantity,
received: scan.amount,
});
}
if (line.reason === 'defective' &&
scan.condition === 'new_with_tags') {
issues.push({ type: 'condition_mismatch', sku: line.sku });
}
}
return {
verdict: issues.length ? 'dispute' : 'accept',
issues,
};
}
Wanneer het oordeel dispute is, stelt de agent een antwoord op naar de Zalando-partner-inbox waarin het RMA-nummer, het Picqer-retour-ID en de specifieke regels in afwijking worden genoemd. De concept-mail blijft in de outbox van de financieel verantwoordelijke staan totdat zij op verzenden drukt.
Verkeerd gelabelde creditnota's, in drie smaken
De €18.400 die we per maand terughalen, valt uiteen in drie faalmodi. Eén ervan hadden we verwacht. De andere twee waren het deel waarvan niemand wist dat het gebeurde.
Smaak één: meningsverschil over staat. De klant retourneerde een jurk in perfecte staat. Onze retourbalie scande hem als nieuw met labels. De inspectie van Zalando beoordeelde hem als defect en rekende ons de groothandelsprijs aan. Ongeveer 38% van het teruggehaalde bedrag zit hier. Dit zijn ook de disputes die Zalando het vaakst accepteert, omdat onze scan-log een tijdstempel heeft, gefotografeerd is, en getekend door een met naam genoemde magazijnmedewerker. De auditfoto is het dragende bewijs; als je vandaag beoordeelde retouren niet fotografeert, begin daarmee.
Smaak twee: opgeblazen aantallen. De klant retourneert twee artikelen, de creditnota factureert drie. Soms is dit een pakfout van Zalando (een verdwaald artikel uit een naastliggende retour werd in de onze meegescand), soms is het een reden-code-wijziging halverwege. Ongeveer 31% van het teruggehaalde bedrag. Dit zijn saaie, mechanische disputes die de agent vrijwel zonder oordeel indient.
Smaak drie: spook-SKU's. De creditnota verwijst naar een productcode die we nooit op de oorspronkelijke order hebben verzonden. Dit was de verrassing. Sommige hiervan zijn EAN-mappings die scheef gaan lopen tussen systemen (een SKU is opnieuw uitgegeven onder een nieuwe code, maar de oude code staat nog op de oorspronkelijke pakbon). Andere zijn echte Zalando-fouten. Ongeveer 31% van het teruggehaalde bedrag, en het lastigst om te winnen, omdat je vanuit het verzendmanifest moet redeneren, niet alleen de retourscan. We vonden deze alleen omdat de audit productcodes tegen de oorspronkelijke pakbon legde in plaats van alleen op de retourscan te vertrouwen. Als je reconciliatielogica bij de retourscan begint, zie je ze nooit.
De moeilijkste faalmodus van een email-agent is niet dingen fout doen. Het is ze stilletjes goed doen met lage zekerheid. Als de agent vol overtuiging een creditnota accepteert die hij had moeten markeren, weet je niet dat je het geld kwijt bent. Bouw de audit-log voordat je de conceptbetwisting bouwt.
Waarom een mens nog steeds op verzenden drukt
We sturen disputes niet automatisch naar Zalando. Drie redenen.
Eerst de juridische kant. Een geautomatiseerd dispute is nog steeds een claim tegen een ander bedrijf, en die claim heeft hetzelfde aansprakelijkheidsprofiel of een mens hem nu intypte of een model hem opstelde. De agent mag de mail schrijven. Het bedrijf is alsnog auteur. Het oordeel van de financieel verantwoordelijke betekent dat een mens met naam binnen het bedrijf de auteur van record is op elke claim die het pand verlaat, wat de positie is die je wilt als Zalando ooit terug gaat duwen op een patroon van disputes.
Ten tweede, stille drift is de faalmodus die het meeste kost. Als de agent stilletjes creditnota's begint te accepteren die hij had moeten markeren, verschijnt het verlies weer op de winst-en-verliesrekening en zien de logs van de agent er gezond uit. Er is geen fout om op te alarmeren. De enige betrouwbare manier waarop we dit vangen, is door een mens in de loop te houden. De financieel verantwoordelijke leest niet elke geaccepteerde RMA, maar ze leest wel elk dispute, wat betekent dat ze de moeilijkste output van de agent elke werkdag ziet. Drift wordt opgemerkt in week één, niet in kwartaal één.
Ten derde is er een culturele reden. De financieel verantwoordelijke was bang dat de agent haar zou vervangen. Het tegenovergestelde gebeurde. Ze besteedt nu 30 minuten per dag aan het beoordelen van dispute-concepten, wat het deel van het werk is dat oordeel vraagt, en haar vrijdagmiddagen krijgt ze terug. De directeur zei het direct toen we overdroegen: het doel was nooit om de rol weg te halen, alleen de slechtste uren ervan. De directeuren die we tegenkomen die er anders over denken, raken hun beste operations-mensen meestal binnen een kwartaal kwijt.
Resultaten na acht weken
De cijfers uit de eerste acht live weken, afgezet tegen een baseline van zes weken ervoor:
- 1.820 RMA's per week gereconcilieerd, gemiddeld. De 20 extra bovenop de oorspronkelijke 1.800 zijn een artefact van groei in het Zalando-kanaal, niet van de agent.
- €18.400 per maand teruggehaald, gemiddeld. De laagste maand was €15.900, de hoogste €21.300 (een zware Black Friday-retourgolf die laat werd verwerkt).
- 4,1% van de disputes door Zalando afgewezen. Lager dan we hadden verwacht. De Picqer-scanlog is overtuigender bewijs dan we hem hadden ingeschat.
- Tijd van de financieel verantwoordelijke aan RMA's: 35 minuten per dag, van een vrijdagmiddag plus een zaterdagochtend om de week.
- Kosten van de agent: ongeveer €240 per maand aan modelcalls en infra, tegen €18.400 teruggehaald. De ROI-rekensom is niet subtiel.
Wat we niet hebben gemeten maar wel hadden moeten meten, is de moreelboost. De financieel verantwoordelijke sluit haar vrijdag nu af met de creditnota's afgehandeld, niet geslikt.
Toen we de email-agent voor deze Tilburgse klant bouwden, was het deel dat ons verraste hoe vaak het dispute werd gewonnen op de kracht van alleen de Picqer-scan. De les die we meenamen naar de volgende twee AI-agent-projecten was om te beginnen met het schrijven van de audit-log, niet de conceptbetwisting. Al het andere volgt uit het kunnen bewijzen wat er werkelijk is gebeurd.
Het kleine ding dat je vandaag kunt doen
Als je Zalando-retouren verwerkt op enig serieus volume, trek dan zes weken aan creditnota's uit je systeem en leg ze met de hand naast je magazijnsysteem. Bouw nog niets. Tel alleen de afwijkingen en zet er een prijskaartje aan. Bij deze klant was het afwijkingspercentage ongeveer 11% op volume en 4% op waarde. De meeste teams waar we deze oefening mee deden, landen ergens tussen 6% en 14% op volume, met de waarde-gewogen schijf grofweg een derde daarvan. Zodra je die twee getallen hebt, weet je of een agent de moeite waard is om te bouwen of dat je al binnen de tolerantieband van Zalando zit.
Kern
Het punt van een email-agent is niet om de mens weg te halen, maar om diens oordeel te plaatsen waar het het meeste geld oplevert. Hier was dat bij elk dispute-concept.
FAQ
Hebben we specifiek Picqer nodig, of werkt elk WMS?
Elk magazijnsysteem dat inkomende retourdata via een API ontsluit, werkt. We hebben varianten gebouwd op Channable, Logic4 en een custom MySQL-backend. Picqer is gewoon wat deze klant toevallig draaide.
Hoe lang duurde de build?
Drie weken van kick-off tot live. Eén week aan de historische audit zodat we het probleem konden beprijzen, twee weken aan de agent zelf plus de dispute-templates en de review-UI voor de financieel verantwoordelijke.
Wat gebeurt er als de agent een fout dispute opstelt?
Zalando wijst hem af en betaalt de oorspronkelijke creditnota. Er is geen dubbele afschrijving en geen boete. De keerzijde van een fout dispute is één verspilde email, geen sanctie, en dat is deels waarom de ROI-rekensom klopt.
Zou je de disputes ook automatisch kunnen versturen?
Technisch wel. We doen het niet, omdat de dagelijkse menselijke review modeldrift vroeg vangt en omdat de met naam genoemde financieel verantwoordelijke het auteurschap draagt van elke claim die het pand verlaat.
Werkt dit ook voor Bol.com- of About You-retouren?
Ja, met andere parsers. De architectuur (mailbox lezen, extraheren, joinen tegen je WMS, dispute opstellen) is identiek. Wat per marketplace verandert, zijn de creditnota-formaten en de dispute-endpoints.