A shipping notifier on AWS for a few dollars a month
A small business ships more orders than anyone keeps in their head. The package that left the warehouse this morning and the customer doesn’t know yet. The order that’s out for delivery today and would love a heads-up. The one that was supposed to arrive Tuesday and is now sitting in a depot two states away while the customer wonders if it’s lost. Keeping every customer in the loop is the kind of work that feels small until you have fifty orders in flight and an inbox full of “where is my order?” This post walks through the design of a small notifier that watches every order, sends the right friendly update at each step, and warns both the customer and you the moment a delivery runs late.
Key takeaways
- Three sources for orders: a Drive sheet from your store, a carrier webhook lane, and an inbox forwarding lane.
- Every order ends in one of five moves on each check: nothing, shipped, out for delivery, delivered, or delayed.
- One update per status, ever. The notifier writes down each send so it never repeats one.
- Updates respect quiet hours and let the customer unsubscribe; a delay warns both customer and owner.
- Designed on AWS for about $2.40/month at typical small-business volume.
The whole system on one page
Before any code, here’s the shape of what we’re designing.
What you set up once (the outside)
- Open orders. A Google Sheet in a Drive folder, one row per order: number, customer name, customer email, carrier, tracking number, current status (placed, shipped, out for delivery, delivered, delayed), expected delivery date, an unsubscribed flag, and a link to the order. You export this from your store once and let new orders and tracking updates flow in from two other lanes covered in Part 2 — a carrier webhook lane (the carrier posts each tracking change to a small endpoint) and an inbox-forwarding lane (forward a carrier email to a dedicated address and the notifier reads it into a proposed status change for one-tap approval).
- A rules folder. Two short Google Docs in a Drive folder. The rules doc covers the quiet hours (when not to email), the expected-delivery windows per carrier or shipping method (so the notifier knows when an order is running late), the owner who gets delay alerts, and the unsubscribe policy. The voice doc holds one update template per shipping step — the “it shipped,” the “it’s out for delivery,” the “it arrived,” and the “it’s running late” message.
- Customers and owner. The customer receives the friendly updates as their order moves along. The internal owner receives the alert when a delivery runs late. Updates land with the order number, the new status, a tracking link the customer can click to see details, and an unsubscribe link so anyone who’d rather not hear about it can opt out in one tap.
What runs on every check (the inside)
- The order intake. Three sources feed the list. The Drive sheet is the canonical store. New orders and tracking updates can also be added via the carrier webhook lane (the carrier posts each tracking change to a small endpoint the moment it happens) and the inbox forwarding lane (forward a carrier email to
tracking@your-company.com, the notifier uses Bedrock Haiku 4.5 to read the email and pull out the order and the new status, then drops a one-tap approval card in the team’s Slack to confirm before the row is updated). - The notifier. Runs on a schedule (every 30 minutes during the day). Reads the open orders. For each one, it looks at the current status and compares it to the last status it told the customer about. Picks one of five moves. Nothing: status hasn’t changed since the last update — do nothing. Shipped, out for delivery, delivered: the status moved forward — send the matching update once. Delayed: the order passed its expected delivery date without arriving — warn the customer and alert the owner. The notifier itself doesn’t call a model on the check — the move logic is plain Python.
- The sender. Reads the voice doc, formats the update message for the chosen move, and sends it. Email goes through SES outbound. It honors quiet hours (no sends between 9pm and 8am local by default) and checks the unsubscribe flag before every send. Every send writes a row in DynamoDB so the next check can tell which updates already went out and never repeats one. A monthly summary writes a one-paragraph narrative: orders shipped, orders delivered, average days in transit, and how many ran late.
In plain words
Jordan ordered a pair of boots and chose standard shipping. The warehouse hands the parcel to the carrier on Monday afternoon. Within the half hour, the carrier’s webhook tells the notifier the status is now shipped, and on the next check Jordan gets a friendly email: “Good news — your order #5120 is on its way! Track it here.” Wednesday morning the parcel goes out for delivery; Jordan gets a quick “it’s out for delivery today” note. Wednesday evening it’s delivered, and a short “it arrived — enjoy!” goes out. Jordan never had to ask. Now picture the other order: it was due Tuesday and Wednesday comes and goes with no delivery. The notifier marks it delayed, sends Jordan a calm heads-up with what it knows, and tells the owner so a human can chase the carrier — before Jordan has to email asking where it is.
The cost of running this is about $2.40 a month at SMB volume. The cost of not running it is the inbox full of “where is my order?” emails, the customer who assumes the worst because nobody told them anything, and the late delivery you only find out about when it’s already a complaint.
Design rules that shaped every decision
- Every update ships with full context — order number, new status, tracking link, unsubscribe link. The customer never has to dig.
- Five moves, always. Nothing, shipped, out for delivery, delivered, delayed. There is no sixth.
- One update per status. The same order never gets two “shipped” emails, even if the carrier reports it twice.
- Quiet hours and unsubscribes are respected. An update is a finite resource; bad timing burns it.
- A delay warns both the customer and the owner, so a human can act before it becomes a complaint.
- Every send is logged. Audit an order next month and you can see every update that went out.
Why this shape
Most small shops handle shipping updates in one of three ways: a person who copies tracking numbers into emails when they have time, the carrier’s own notifications that customers ignore because they look like spam, or nothing at all. The person works until they’re busy — and the busiest weeks are the ones with the most orders in flight. The carrier’s emails come from an address the customer doesn’t recognize, in a voice that isn’t yours. And “nothing at all” is how a happy customer turns into an anxious one who fills your inbox.
The setup above keeps the order list in a sheet the team already exports, but adds a small system that looks at that list on a schedule and acts only when an order has actually moved. Updates go out in your voice, with your branding, carrying a tracking link so checking is one click. They go out once per step, never repeated. They respect quiet hours so nobody gets a 2am ping. And they catch a late delivery early enough that a human can step in. The notifier is invisible most of the time; it only speaks up when there’s genuinely something the customer wants to know.
The next four posts walk through each piece in turn: how an order gets watched, how a shipping update gets sent, how a message reaches the customer, and how a late delivery gets caught. One diagram per post. A cost breakdown and a final engineering reference at the end.
All posts