Email automation
Email agent for RFQs: a fastener distributor in Nijmegen
Monday at 07:45 in Nijmegen. The sales floor inbox holds 247 unread RFQs in three languages, all needing line-item quotes against a 17-year-old Ridder iQ. The agent has 90 seconds per thread.

The Monday inbox at the distributor's office near Bijsterhuizen sits at 247 unread RFQs by 07:45. Some are from Düsseldorf machine shops asking for prices on 4,000 M10 hex bolts in grade 8.8, zinc-plated. Some are from a yacht builder in Friesland needing A4 stainless socket caps in tiny quantities. A handful are forwarded from purchasing portals at Tier-2 automotive suppliers, threaded into PDF attachments that nobody on the sales floor wants to open before coffee.
The team is forty-one people. The catalogue covers roughly 38,000 active SKUs. The ERP is Ridder iQ, installed in 2009, with custom modules that nobody at the original implementation partner still works on. Most quotes used to take a salesperson between eight and twenty minutes to assemble, depending on how many parts they had to look up by hand and whether the customer's part numbering matched the in-house numbering or not.
Then there are the German and English threads. Roughly 38% of the inbound RFQs are not in Dutch.
We were asked to look at this in February. The brief was simple. The team was burning a day a week per salesperson on quote drafts. Could an agent do the first pass?
This is what we built, what broke, and what 90 days of production looked like.
The volume problem before the agent
A weekly average of 1,840 RFQ threads lands in info@ and the four sales-rep inboxes combined. That number includes follow-ups, clarifications, and re-quote requests, not just first-touch RFQs. Roughly 60% of the threads are in Dutch, 30% in German, 10% in English.
The shape varies wildly:
- A clean line-item list pasted into the email body.
- A scanned PDF with handwritten quantities in the margin.
- A forwarded Excel with five tabs and a "ZIE TABBLAD 2" note.
- A two-line message that says "Same as last time but 2x the M8s".
Pre-agent, the sales floor handled this by triaging by sender. Known accounts went to the rep who owned them. Unknown senders went into a shared queue that, on a bad week, ran 30 hours behind. The quote conversion rate was decent on accounts with named owners. On the shared queue it cratered, because by the time anyone replied, the customer had already ordered from a competitor.
The Ridder iQ constraint
Ridder iQ is a Dutch ERP that has been in the wholesale and manufacturing market since the late nineties. Solid system. The price book lives there. The customer-specific staffeling-korting (volume tier discount) lives there. The SKU master, the lead-time data, the credit holds, the open-order book, all of it. You cannot answer an RFQ correctly without it.
The integration story for a 17-year-old install is what you would expect. There is a SOAP endpoint that the previous integrator wired up around 2014 for a webshop project. There is a SQL replica that runs on a nightly job. There is no documented REST API. There is no published schema for the staffeling tables. There is a 380-page PDF from the original implementation that explains the ERP screens but not the data model behind them.
We picked the SQL replica as the read path and a small Node service in front of the SOAP endpoint as the write path. The SOAP service handles creating a draft sales order; we never let the agent post it as confirmed. That gate matters later.
The per-thread pipeline
Each inbound email runs through the same pipeline. We give ourselves a 90-second budget per thread from arrival to drafted reply.
- Language detection and intent classification. Is this an RFQ, an order, a delivery query, an invoice question, or noise? Roughly 71% of the inbound mail is RFQs. Orders and delivery queries route to different agents that we will not cover here.
- Part extraction. Parse the body, OCR any attached PDFs, read any attached Excel. Resolve customer part numbers against the in-house catalogue using a vector lookup over historical thread pairs plus the official cross-reference table. This is where most of the gnarl lives. A customer who writes "M10x40 8.8 verzinkt" and a customer who writes "BOLT HEX M10 40MM CL8.8 ZN" want the same SKU, and both are asking for what the spec sheets call ISO 4014.
- Account resolution. Match the sender domain against the Ridder customer table. Pull credit status, payment terms, staffeling tier, and any account-specific notes.
- Pricing. For each resolved line, look up the base price and apply the customer's staffeling-korting at the requested quantity. Volume tiers are not linear; the customer asking for 4,000 M10 bolts sits in a different bracket than the customer asking for 800.
- Draft reply in the sender's language. Three templates, three tones. Dutch is direct. German is formal and explicit about delivery terms. English sits somewhere in between.
- Approval gate. If the total order value is under €25,000 and every line resolved cleanly, the draft lands in the rep's outbox as a ready-to-send reply. If it is over €25,000, or if any line failed to resolve, the thread goes to the sales-manager queue with a flag explaining why.
The 90-second budget is not a marketing number. It is the SLA we picked because the Düsseldorf machine shops measure response time and quote a competitor at the 10-minute mark.
The staffeling-korting math
This is the part nobody outside Dutch wholesale cares about, and it is the part that took the longest to get right.
Most accounts have a base discount off list, then a quantity multiplier at break points like 250, 1000, 5000, and 25000 pieces per SKU. Some accounts have a mix discount where you add up quantities across a SKU family (all M10 8.8 hex bolts, regardless of length) and apply the bracket to the combined volume. A handful of strategic accounts have hand-negotiated grids that live in a custom Ridder table the original implementer added in 2017.
The agent does not try to be clever about this. It reads the rule for the account, applies it, and shows its work in the draft reply as a comment block that the rep sees but the customer does not.
// Internal note (not sent)
// Account: 10421 Bauer GmbH, Düsseldorf
// Staffeling: family-mix, brackets at 1000 / 5000 / 25000
// Line 1: M10x40 8.8 ZN, qty 4000
// Family qty across thread: 11200 (incl. lines 2, 3)
// Bracket hit: 5000, multiplier 0.78
// Base €0.142, final €0.111 per piece
// Line 2: M10x50 8.8 ZN, qty 4000
// Line 3: M10x60 8.8 ZN, qty 3200
The rep does not have to recompute. They glance at the comment block, decide it looks right, and hit send.
The €25k approval gate
The first version of the agent did not have this. We thought a tight SLA on draft quality plus rep review would be enough. It was, for small orders. For anything over about €15,000, the reps started spending more time second-guessing the draft than they had previously spent writing one from scratch.
We added the €25,000 threshold based on the distribution of historical orders. Roughly 6% of RFQs cross it. Those go to the sales manager, who reviews the agent's work, the staffeling logic, and the customer's recent payment behaviour before the quote leaves the building.
This is a feature, not a workaround. Nobody at the distributor wants an agent that emails a 40-piece carbon-steel quote to a Tier-1 automotive buyer without a human between the model and the customer. A human signs off above a threshold, and the threshold is set by the business, not by the model.
The 90-second SLA is the actual product. The model is a component. The product is a draft on the rep's screen before the customer has finished their coffee.
Three things that broke in production
In order of how much they hurt.
Custom part numbering
A customer in Twente has been ordering the same six SKUs for nineteen years using their own internal codes. We had to seed the cross-reference table with their historical orders before the agent stopped misclassifying them as new parts. There was no spec sheet for the mapping. The mapping lived in the head of the rep who had worked the account since 2007.
Multilingual signatures
Outlook signatures in the German threads kept getting OCR'd as part of the part list. "Geschäftsführer Hans Müller" briefly became a request for a quote on a SKU called MÜLLER. We patched the parser to skip anything below the first -- or Mit freundlichen Grüßen, with a fallback heuristic for unsigned threads.
PDF orientation
Some scanned RFQs come in sideways. The OCR step now rotates and re-tries before extracting. Obvious in hindsight, expensive on the day a €31,000 quote went out with a missing column.
Ninety days in production
The agent has been live since 13 March. Through 14 June it has processed 23,840 RFQ threads.
- 71.4% drafted and sent by the rep with no edits beyond a greeting line.
- 22.1% drafted and sent after a small edit (usually a lead-time note or a delivery-week change).
- 4.3% rewritten by the rep before sending.
- 2.2% sent to the sales-manager queue under the €25k rule.
Median response time on first-touch RFQs went from 11 hours to 38 minutes. The sales floor recovered roughly 4.5 days of senior-rep time per week, which is now spent on outbound and on the strategic-account grids.
The lever for your own inbox
If you are running quotes off an ERP that predates your salespeople, the work is not in the model. The work is in four hard things in order: reading the ERP without breaking it, resolving customer part numbers against your master, computing the right discount tier for the right account, and putting a human gate at the value where a mistake actually hurts.
When we built the email agent for the Nijmegen distributor, the thing we ran into was that the Ridder iQ staffeling tables had no documented schema and three accounts used a custom mix-bracket rule nobody on the current team knew about. We solved it by spending two weeks shadowing the senior rep before we wrote a line of integration code.
The five-minute audit, if you want to see whether your inbox is a candidate: pull the last four weeks of mail to your sales-team alias, sort by language, count how many threads are first-touch RFQs, and check your median time-to-first-reply. If that number is over four hours and your customers are buying from competitors at the eight-minute mark, the gap is the product.
Key takeaway
The 90-second SLA is the product. The model is a component. A draft sits on the rep's screen before the customer's coffee gets cold.
FAQ
How does the agent handle customer part numbers that don't match the internal catalogue?
It runs a vector lookup over the customer's historical orders plus the official cross-reference table. New customers with no history get manually mapped by a rep on first contact, and the mapping is then learned.
Why a €25,000 approval threshold instead of a percentage of order value?
The number came from the distribution of historical orders. Roughly 6% of RFQs cross it, which puts the right load on the sales manager without making the gate a bottleneck.
Can this work with an ERP that has no API at all?
Yes, but you need a read path. A SQL replica, a nightly export, or a SOAP endpoint all work. The agent has to read the price book and customer terms live, or the quotes will be wrong.
What does the agent do with PDF and Excel attachments?
PDFs go through OCR with rotation correction. Excels are parsed sheet by sheet using a tab-name heuristic. Both feed into the same line-item extraction step as the email body.