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

How a feedback reply comes back

The ask went out. Now the customer either taps a star or types a few words — or does nothing. A tapped star arrives as a click on a one-tap link, which hits a Lambda Function URL. A written reply arrives as an email, which SES inbound catches. Either way, the same small Lambda records the reply exactly once, marks the customer as answered so they’re never asked again this cycle, and hands the reply to the router. The whole path is plain Python; no model is needed just to receive a reply.

Key takeaways

  • A star tap is a one-tap link click that hits a Lambda Function URL — no app, no login.
  • A written reply arrives by email and is caught by SES inbound.
  • Each reply carries a signed token, so the system knows exactly which ask it answers.
  • DynamoDB records the reply once and marks the customer answered, so no ask goes out twice.
  • Receiving a reply uses no model. The mood read in the next post is the only AI step.

The reply flow, per customer

How a reply is received, deduped, and recorded A vertical decision flow diagram. At the top, an input box "Reply arrives" — either a star-tap link click or an email reply, each carrying a signed token that names the ask. Below that, a step "Verify the token" — check the signature and look up which customer and which ask this answers. Below that, a check "Already answered?" — if yes, route to "Ignore" (do nothing; the customer already replied). If no, continue. The next step "Read the reply payload" — pull the star count from the link, or the email body text. The next step "Mark customer answered" — write to the fc-feedback table so no further ask or follow-up goes out for this cycle. A reply whose token can't be matched also routes to "Ignore" and is logged. The next check "Star tap or free text?" — if a star tap, route to "Record score" with the number. If free text, route to "Send to router" for a mood read. If the reply couldn't be parsed at all, route to "Hold for human." Each terminal box — Ignore, Record score, Send to router, Hold for human — leaves a clean record. A note at the bottom: a reply is recorded exactly once; the token makes every reply traceable to one ask and one customer. Reply arrives star tap · or email · signed token Step 1 Verify the token which customer, which ask Step 2 Already answered? read DDB fc-feedback table Step 3 Read the reply payload star count, or email body text Step 4 Mark customer answered no more ask → for this cycle no follow-up → either Step 5 Star tap or free text? decide the next hop Ignore already replied Record score star count saved Send to router free text, read mood Hold for human couldn’t parse if yes no token unparseable tap text A reply is recorded exactly once — the token ties every reply to one ask and one customer.
Fig 3. The reply flow, per customer. Five steps verify, dedupe, record, and route. A star tap goes straight to a recorded score; free text goes to the router for a mood read; anything unparseable waits for a human.

The ask message ends with five stars, and each star is a plain web link. There’s no app to install and no login — tapping a star simply opens a link. The link points at a Lambda Function URL and carries two things in the address: how many stars the customer tapped, and a signed token. The token is a short string the system generated when it sent the ask, signed with a secret so it can’t be faked or guessed. It names exactly which customer and which ask this tap answers.

When a star link is tapped, the reply-handler Lambda runs. It checks the signature on the token (a tampered or stale token is rejected and logged), looks up the customer, and reads the star count straight from the link. Five stars and four stars are happy; one and two are unhappy; three is unclear. The handler shows the customer a simple thank-you page, and — for a happy tap — can include the review link right there, so a five-star tapper sees the public-review button immediately while their goodwill is highest. The routing details are Part 5; the point here is the tap is received, verified, and recorded in one quick step.

The written reply

Some customers don’t tap. They hit reply on the email and type a sentence: “loved it, the staff were lovely,” or “waited far too long and the coffee was cold.” That reply lands in a dedicated inbox — feedback@your-company.com — handled by Amazon SES inbound. SES writes the raw message to S3, which triggers the same reply-handler Lambda. The Lambda finds the signed token in the email’s reply headers (it was tucked into the address the ask was sent from), identifies the customer, and pulls out the body text.

A star tap is unambiguous — a number is a number. A written reply isn’t; “it was fine I suppose” could be a quiet five or a polite two. So a written reply doesn’t get scored by simple rules. It gets handed to the router, which reads the mood of the words. That step — the one place the system uses a model — is the whole of the next post.

Recorded once, asked once

Both paths converge on one rule: a reply is recorded exactly once, and a customer who has replied is never asked again this cycle. The moment a reply is verified, the handler writes a row to the fc-feedback DynamoDB table keyed by the customer and the ask. If a second reply arrives for the same ask — the customer tapped a star and then also wrote an email, or double-tapped — the “already answered?” check catches it and the later reply is ignored (the first one stands; the duplicate is logged for the record).

The same row is what stops the follow-up. The daily sweep that sends gentle reminders skips any customer whose row shows a reply. So the unbreakable promise from Part 1 — ask once, never nag — is enforced by a single row in a single table, written the instant a reply comes in.

Next post: how the router reads a reply — the star rule for taps, the quick Bedrock mood read for free text — and turns every reply into happy, unhappy, or unclear.

All posts