Part 1 of 7 · Feedback collector series ~5 min read

A feedback collector on AWS for a few dollars a month

A happy customer who walks out the door is worth a public review — if you ask at the right moment. An unhappy one is a bad review waiting to happen — unless you hear about it first and fix it privately. Most small businesses do neither: they never ask, or they blast everyone the same generic “rate us” email and end up sending their grumpiest customers straight to a one-star page. This post walks through the design of a small system that asks each customer one simple question at the right time, reads the answer, and sends the happy ones toward a public review and the unhappy ones straight to the owner.

Key takeaways

  • Three sources for customers to ask: a Drive list, a point-of-sale webhook lane, and a booking import lane.
  • Every reply lands in one of three buckets: happy, unhappy, or unclear.
  • Happy goes to a public-review nudge; unhappy goes to the owner privately; unclear waits for a human.
  • Asks respect quiet hours and your holiday calendar. It asks once and never nags.
  • Designed on AWS for about $2/month at typical small-business volume.

The whole system on one page

Before any code, here’s the shape of what we’re designing.

System shape: three sources, three pieces inside AWS At the top, three external boxes in a row. Far left, "Customers" — the list of people who just finished a visit or purchase, drawn from a Google Drive sheet, plus a point-of-sale webhook lane and a booking import lane that add new completed visits. Centre, "Rules and voice" — a Drive folder with a rules doc covering per-type timing windows, quiet hours, and the owner, plus a voice doc with the ask message and the follow-up templates. Far right, "Owner and review links" — where a happy nudge points (your public Google or Facebook review link) and where an unhappy reply lands (the owner, privately). Each connects via an arrow to the AWS account container below. Customers have an outgoing arrow into AWS and replies flow back. Rules and voice feed in to ground every ask. The owner receives any unhappy reply with the customer's name, what they bought, their words, and a one-tap way to reach back. Inside the AWS account are three components in a row, mirroring the layout above. On the left, the Request builder — receives finished visits from each source, picks the right send time from the rules doc, and sends one simple ask. In the middle, the Router — reads the reply, reads the mood, and sorts it into happy, unhappy, or unclear. On the right, the Dispatch — sends a review nudge to happy customers, pings the owner privately for unhappy ones, and holds unclear ones for a human. Internal arrows flow left to right. A note at the bottom reads: happy goes public, unhappy goes private — the system never sends an unhappy customer toward a review page. Customers list, POS, bookings Rules and voice timing, owner, templates Owner + reviews where replies land ask out grounds routed by mood AWS account Request builder pick the moment, send one simple ask Router reads the reply, happy, unhappy, unclear Dispatch review nudge or private owner ping reply move Happy goes public, unhappy goes private — the system never sends an unhappy customer to a review page.
Fig 1. Three sources outside, three pieces inside AWS. Customers flow in from a Drive list, a point-of-sale webhook lane, and a booking import lane. The Router reads each reply and picks one of three buckets. Dispatch sends the right move to the right place.

What you set up once (the outside)

  • Customers. A Google Sheet in a Drive folder, one row per finished visit or purchase: customer name, contact (email or mobile), what they bought or booked, the type of visit (sit-down meal, takeaway, delivered product, service appointment), the date it happened, and the staff member who served them. You can fill it in by hand, but in real life it fills itself from two other lanes covered in Part 2 — a point-of-sale webhook lane (your till or checkout tool posts a row the moment a sale closes) and a booking import lane (finished appointments get pulled in from your calendar each hour).
  • A rules folder. Two short Google Docs in a Drive folder. The rules doc covers the timing for each visit type — how long to wait before asking. A sit-down meal might ask two hours later, while the coffee’s still warm in memory; a delivered product waits three days so it has actually arrived; a service appointment asks the next morning. The doc also names the owner who gets unhappy replies, the quiet hours, any holidays to skip, and the public-review link a happy nudge should point to. The voice doc holds the ask message and the one gentle follow-up — what the email or text actually says.
  • Owner and review links. The owner is the person who personally handles an unhappy customer — usually you. The review link is your public Google, Facebook, or industry-site page. Happy nudges point there; unhappy replies never do. They go to the owner with the customer’s name, what they bought, their exact words, and a one-tap way to call or message them back.

What runs after every visit (the inside)

  • The request builder. Three sources feed the customer list. The Drive sheet itself is the canonical store. New rows also arrive via the point-of-sale webhook lane (your checkout tool calls a small endpoint when a sale closes) and the booking import lane (finished appointments get pulled hourly by a small sync Lambda). For each new finished visit, the builder reads the rules doc, works out the right moment to ask, and schedules one simple question to go out then — not now, not three times, just once at the right time.
  • The router. When a reply comes back, the router reads it. A one-tap star rating is read by plain Python: four or five stars is happy, one or two is unhappy, three is unclear. If the customer wrote a few words instead of tapping, a quick Bedrock Haiku 4.5 read decides the mood from the words. Either way the reply lands in exactly one of three buckets: happy, unhappy, or unclear. The router writes the result to a small table so the same customer is never asked or routed twice in one cycle.
  • Dispatch. Reads the voice doc and acts on the bucket. Happy: send a short, warm note thanking them and offering a one-tap link to leave a public review. Unhappy: skip the review entirely and ping the owner privately, right away, with everything they need to make it right. Unclear: hold it for a human to glance at in the daily sweep. Every move honors quiet hours and the holiday calendar, and every reply and routing decision is logged. A monthly summary writes a short owner-ready paragraph: how many asked, how many happy, how many unhappy, how many turned into reviews.

In plain words

Dwi finishes dinner at your restaurant at 8pm on a Friday. Your till closes her bill and posts a row. The rules doc says sit-down meals ask two hours later, but two hours later is 10pm — inside quiet hours — so the ask waits until 9am the next morning. At 9am Dwi gets a short text: “Hi Dwi, thanks for dining with us last night! How was it?” with five tappable stars. She taps five. The system reads “happy,” sends a warm follow-up — “So glad! Would you mind leaving a quick review? [one-tap link]” — and you get a new five-star review by lunchtime. Meanwhile Budi, who waited 40 minutes for his order the same night, taps two stars and types “food was cold.” He is never shown the review link. Instead your phone buzzes at 9:01am: “Budi — 2 stars — ‘food was cold’ — table 12 last night — [call] [text].” You call, apologize, offer him a free dessert next time. The one-star review that almost happened becomes a regular instead.

The cost of running this is about $2 a month at SMB volume. The cost of not running it is every happy customer who never left the review that would have brought in the next ten, and every unhappy one who skipped you and told the internet instead.

Design rules that shaped every decision

  • Ask at the right moment — timing is in the rules doc, per visit type. A badly timed ask is a wasted one.
  • Three buckets, always. Happy, unhappy, unclear. There is no fourth.
  • Unhappy never sees a review link. The whole point is to catch it before it becomes a public one.
  • Ask once. At most one gentle follow-up, then stop. Quiet hours and holidays are respected.
  • The customer list lives in Drive. Adding a customer or changing the timing doesn’t need a deploy.
  • Every reply and routing decision is logged. You can audit any month’s feedback in one place.

Why this shape

Most businesses handle feedback in one of three ways: they never ask, they ask everyone the same way at the wrong time, or they ask only the customers they already know are happy. The first leaves money on the table — happy customers will leave reviews, but only if you ask, and only if asking is easy. The second is worse than nothing: a generic “rate us” blast sends your unhappy customers straight to the public page where they do the most damage. And cherry-picking only the happy ones is both dishonest and a missed chance — the unhappy customer you avoid asking is the one whose problem you could have fixed.

The setup above splits the job in two on purpose. Ask everyone — but read the answer before you decide what to do with it. A good answer earns a public ask, at the moment it’s easiest to say yes. A bad answer earns a private, immediate heads-up to the one person who can fix it. The system is invisible to a happy customer (one tap, one thank-you, done) and a quiet early-warning radar to the owner. It never nags, never asks twice, and never points a frustrated customer at the page where frustration costs the most.

The next four posts walk through each piece in turn: how a feedback request goes out, how a reply comes back, how feedback gets routed by mood, and how a review ask or a private fix actually happens. One diagram per post. A cost breakdown and a final engineering reference at the end.

All posts