How a purchase request arrives
The fastest way to kill a purchase process is to make people use a system they do not already live in. So this one does not pick a channel. It takes an email, a short web form, or a Slack shortcut, and turns all three into the same five fields. This post is about that conversion, and about the two things that make it trustworthy: it leaves a field blank rather than guessing, and it will not create a second request for the same ask.
Key takeaways
- Three lanes — an inbox, a five-field form, and a Slack shortcut — all land as one request record.
- The record is always the same five fields: requester, item, budget line, amount, vendor.
- A field the reader cannot fill confidently stays blank. Blank is a question; a guess is a bug.
- A fingerprint on requester, vendor, amount and day makes a resent email land on the same request.
- Attachments — a quote PDF, a screenshot of a cart — are kept and shown to the approver.
Three ways in, one shape out
People ask for things the way they already talk. Forcing everyone through one form is how you end up with a form nobody uses and a purchase process that lives in your inbox anyway. So there are three lanes, and they all end in the same place.
- App integration
- Machine learning
- Front-end & mobile
The five fields
Everything downstream — the budget check, the approval message, the purchase order — reads exactly five fields. Keeping the list this short is deliberate. Every extra field is another thing the reader can get wrong and another thing a requester can leave out.
- Requester. Who asked. From the email sender, the form session, or the Slack user. This one is never guessed — it comes from the channel itself, which is also why a forwarded email is treated as a request from the forwarder, not the original sender.
- Item. What they want, in their words, trimmed to one line. “Two boxes of blue nitrile gloves, size L.” Not normalised, not looked up in a catalogue. The approver is a human and reads human sentences faster than SKUs.
- Budget line. Which row in the sheet this belongs to. This is the field the reader gets wrong most often, and so it is the field it is most willing to leave blank.
- Amount. The number, in your currency. If a quote PDF is attached the reader prefers the number on the quote over the number in the email body, because people round in prose and quotes do not.
- Vendor. Who it is being bought from. Matched against the approved-vendor tab by name, loosely — “Medline”, “Medline Industries” and “medline.com” are the same vendor — but a name with no match stays as typed and becomes a question later.
What happens to one email
The email lane is the messy one, so it is worth following end to end.
- Storage
- Database
- App integration
- Machine learning
- Security & identity
- People
Why the duplicate test comes first
People resend. They forward the same request to a second address in case the first one was missed, they reply “any update?” on the same thread, they tap the form submit button twice on a bad connection. Every one of those is the same ask, and every one of them would otherwise become a second purchase order.
So before anything expensive or consequential happens, the intake builds a fingerprint — the requester, the vendor as typed, the amount, and the calendar day — and tries to write it to DynamoDB with a condition that the key does not already exist. The database decides, not a query-then-write in the function, because two copies of the same email arriving a second apart will both pass a query and both write. A conditional write cannot both succeed. The loser reads the winner’s request id and replies on that thread instead.
Putting the test before the model read has a second benefit: a resent email costs nothing. The read is the only line on the bill that scales with volume, so not paying it twice for the same ask matters more than it looks.
Blank beats plausible
The reader is allowed to say it does not know. If the email is “can we get the thing we talked about”, the item field will be the sentence as typed, and the budget line and the amount will be empty. The system does not guess a budget line from the requester’s department, and it does not guess an amount from last month’s order. It replies: “Which budget line, and roughly how much?”
This looks like a worse system on a demo and is a much better one in practice. A guessed budget line is a wrong number in a real budget, discovered at month end, after the money is gone. A blank field is a fifteen-second reply.
What the intake guarantees
- Every request that reaches the checker has all five fields filled, or it never reached the checker.
- One ask produces one request record, even if the same email arrives four times.
- The original message and every attachment are kept, so the approver can open the quote.
- The requester always hears back — a request id, a question, or a purchase order. Never silence.
Next: what the checker actually does with those five fields, and why none of it is the model’s job.
All posts