An order status responder on AWS for a few dollars a month
Every shop that ships anything hears the same question all day: where’s my order? It arrives by email, by SMS, and through the chat box on the website, and answering it is the same dull lookup every time — find the order, check the carrier, say when it’s coming. This post walks through the design of a small responder that does that lookup the moment the question is asked, replies with a real ETA, and quietly hands the hard ones to a person.
Key takeaways
- Three inbound channels: email through SES, SMS through a provider webhook, and the web-chat box on your site.
- Every message is matched to one order — by the order number in it, or by the sender’s email or phone.
- A matched order gets a live carrier lookup; one Bedrock call turns those real facts into one friendly reply with an ETA.
- It never invents a status. Unmatched messages and delayed, lost, or stuck orders go to a person with full context.
- Designed on AWS for about $2.80/month at roughly 500 enquiries a month. It only ever replies — a human handles the rest.
The whole system on one page
Before any code, here’s the shape of what we’re designing. Every shop that ships things fields the same question all day — where’s my order? — and it arrives on whatever channel the customer happens to be on. The work is always identical: find their order, check the carrier, tell them when it’s coming. The system below does exactly that, and nothing more.
What you set up once (the outside)
- Orders and carriers. A Google Sheet in a Drive folder with one row per order: order number, customer name, customer email, customer phone, the items, the carrier, the tracking number, and a status. You already keep this, or something like it, in whatever you sell through; this just puts it where the responder can read it. New messages enter via three channels covered in Part 2 — an email lane (customers reply to your order emails or write to a support address), an SMS lane (your SMS provider posts inbound texts to a webhook), and the web-chat box on your site. The carriers’ own tracking APIs are the second source: they, not the sheet, are the truth about where a parcel actually is.
- A small settings doc. One short Google Doc in the same folder. It holds the things you’ll want to change without a deploy: your business name and the voice for replies, the carriers you use and which account each tracking number belongs to, the “quiet hours” for any outbound SMS, and the rules for when to escalate rather than answer — for example, always escalate anything that reads as a complaint, and always escalate an order with no carrier scan in five days.
- Your team. The person who picks up everything the responder deliberately won’t touch — usually whoever does support. They get an email (or a row in a shared inbox) with the original message, the order the system matched it to, and the raw carrier response, so they can answer with everything in front of them. The responder never refunds, cancels, or reships; those are always a human’s call.
What runs on every message (the inside)
- Intake and match. Whatever channel a message lands on, it ends up as one normalised record: who sent it, on what channel, and the text. The matcher looks first for an order number in the text; failing that, it matches on the sender’s email or phone against the orders mirrored from the sheet. A confident single hit moves straight on. No match, or several open orders for one customer, goes to escalation rather than a guess. This is Part 2.
- Live tracking. With an order in hand, a small Lambda calls the matching carrier’s tracking API using a key from Secrets Manager, and turns the carrier’s own status codes into one normalised picture: in transit, out for delivery, delivered, or a problem. The carrier’s answer — not the sheet, not a guess — is the only source of a status. This is Part 3.
- Reply writer. One Bedrock Haiku 4.5 call takes the real tracking facts — carrier, last scan, location, ETA — and writes a single friendly reply in your voice, sent back out on the same channel the customer used. The model is handed the facts and told to use only those; if there are no facts, there is nothing to write and the order is escalated instead. This is Part 4.
- Escalation. The lane for everything that shouldn’t get an automatic reply: an unmatched message, an upset customer, or an order the carrier shows as delayed, lost, or stuck. It also runs as a daily sweep that re-checks in-flight orders and flags the ones going quiet before the customer has to chase. This is Part 5.
In plain words
A text comes in to your shop’s number at 9:12 on a Saturday: “Hi, any update on order 10482?” The responder finds order 10482 in the mirrored sheet — one item, shipped two days ago with Royal Mail, tracking AB123456789GB. It calls Royal Mail’s tracking API: the parcel was scanned at the local delivery office at 7:40 that morning, out for delivery today. One Bedrock call turns that into: “Hi Sam — your order 10482 is out for delivery today with Royal Mail and should arrive by this evening. You’ll get it at the address ending Flat 2B. Anything else I can help with?” The customer has an honest answer within seconds of asking, on the channel they used, and nobody on your team touched it.
The same afternoon a different text arrives: “order 10090 still hasn’t turned up and it’s been two weeks, this is ridiculous.” The responder matches the order, sees the last carrier scan was eleven days ago, and reads the tone. It sends nothing automatic. Instead it lands in your support inbox with the message, the order, and the tracking history attached, flagged delayed — customer upset, so a person picks it up with the full picture and decides whether that’s a reship, a refund, or a call to the carrier.
Design rules that shaped every decision
- One question, one job. The responder answers “where’s my order?” and does nothing else — no refunds, no cancellations, no reships.
- The carrier is the only source of a status. The responder reports what the tracking API says, and never makes a status up.
- A confident match or nothing. Ambiguous and unmatched messages go to a person, not to a guess.
- The model only writes words. Matching and tracking are deterministic; Bedrock is handed real facts and told to use only those.
- Settings live in a doc. Voice, carriers, quiet hours, and escalation rules change without a deploy.
- It never goes quiet on a problem. Anything delayed, lost, stuck, or upset is escalated to a human with the full context.
Why this shape
Most small teams answer “where’s my order?” one of three ways: someone stops what they’re doing to look it up, the message sits unanswered until someone has a quiet moment, or there’s a canned “please allow 3–5 working days” auto-reply that tells the customer nothing. Looking it up by hand is fine until twenty come in at once. Leaving it is how a one-line question becomes a one-star review. And the canned reply is worse than silence, because it pretends to answer while skipping the only part the customer cares about — the actual parcel.
The shape above keeps the sheet you already maintain as the list of orders, leans on the carrier’s own tracking as the source of truth, and adds a small system that does the lookup the instant the question is asked. The 80% of enquiries that are simply “is it coming?” get an honest, specific answer in seconds. The few that are genuinely going wrong are pulled out and handed to a person early, with everything they need to act — while there’s still time to fix it.
The next four posts walk through each piece in turn: how a message gets matched to an order, how live tracking gets pulled, how a status reply gets written, and how a stuck order gets escalated. One diagram per post. A cost breakdown and a final engineering reference at the end.
All posts