Part 5 of 7 · Quote follow-up series ~5 min read

How a reply stops the follow-ups

A reply from Dave lands in the quotes@ inbox at 8:03am: “Looks good — can we start the week of the 20th?” What happens next? The honest answer is “it depends on what the customer actually said.” This post walks through the three things the system can do when a reply comes in — stop and hand off, defer, or, when no reply ever comes, close the quote out at expiry — and how the list, the chain state, and the audit trail all stay in sync.

Key takeaways

  • Three outcomes per reply: stop & hand off (accept, decline, or question), defer (out-of-office), and the expiry close-out.
  • Bedrock Haiku 4.5 reads the reply and sorts it; a person always handles the real conversation.
  • Accept, decline, and question all stop the chain and notify the rep with the reply attached.
  • Out-of-office is the only reply that doesn’t stop the chain — it just pushes the next nudge.
  • Every reply and every close-out writes a row to the audit trail.

Three outcomes on a reply

Three outcomes when a customer replies A diagram showing one input on the left flowing through a small classify step, then branching into three outcome paths. Far left: an "Incoming reply" box showing a typical customer email arriving at the quotes inbox via SES inbound — the customer's message, the quote it answers, and the date. A small classify step reads the reply with Bedrock Haiku 4.5 and tags it as accept, decline, question, or out-of-office. The middle column shows the three outcomes. Outcome one, Stop and hand off: this covers accept, decline, and question. A Function URL Lambda writes a reply row to the qf-reply DynamoDB table, which moves the quote to resting forever so no more nudges fire, then emails the rep the full reply with a clear label — looks like a yes, looks like a no, or has a question — so a person takes the conversation from here. Outcome two, Defer: the reply was an out-of-office auto-response. The Lambda reads the away-until date from the message, writes a defer row to qf-reply that suppresses nudges until after that date, and does not hand off — the chain simply resumes once the customer is back. Outcome three, Expiry close-out: no reply ever came and the quote passed its expiry date. The daily timer writes the quote to a separate closed sheet in the Drive folder with the date it expired and the last-known status, and stops nudging. The right side shows the convergence: every outcome writes a row to the qf-audit DynamoDB table with timestamp, quote id, outcome, by-user or by-system, and notes. A note at the bottom: only out-of-office keeps the chain alive — every real reply stops it and goes to a person. Incoming reply SES inbound → classify accept decline / question out-of-office Outcome 1 Stop and hand off • accept, decline, question • qf-reply moves to resting • chain stops for good • rep gets the full reply Outcome 2 Defer • out-of-office reply read the away-until date • suppress nudges until the customer is back Outcome 3 Expiry close-out • No reply ever came and the quote expired • Written to closed sheet — nudging stops Audit trail DynamoDB qf-audit timestamp · quote_id outcome · by-whom notes Only out-of-office keeps the chain alive — every real reply stops it and goes to a person.
Fig 5. Three outcomes per reply, three different effects. Stop and hand off ends the chain and sends the reply to the rep. Defer pauses the chain until the customer is back. The expiry close-out files a quote that never got an answer. Every outcome writes to the audit trail.

Reading the reply (a small, careful classify)

When the customer replies, the email lands in the quotes@your-company.com inbox — because Part 4 set that as the reply-to on every nudge. SES inbound writes the raw message to S3, which triggers a reply Lambda. The Lambda pulls the message text and the quote it belongs to (the subject line and a hidden reference tag both carry the quote id) and calls Bedrock Haiku 4.5 with one job: tag the reply as accept, decline, question, or out-of-office. The prompt is narrow: “Read this reply to a quote. Return one label only. If it’s an automatic away message, say out-of-office. Do not summarize, do not reply.”

The model only sorts the reply. It never writes back to the customer and never decides the deal. That’s the line: AI is allowed to read and route, but a person handles every real conversation.

Outcome 1: stop and hand off (accept, decline, or question)

Three of the four labels — accept, decline, and question — all do the same structural thing: they stop the chain and hand the deal to a person. A Function URL Lambda writes a row to qf-reply with (quote_id, reply_date, outcome), which the timer reads in the “already replied?” check from Part 3 and treats as resting forever. No more nudges fire on that quote.

Then the rep gets an email with the full customer reply and a clear label at the top: looks like a yes, looks like a no, or has a question. A “yes” is the rep’s cue to send paperwork or schedule the work. A “no” is a chance to ask what changed, or just to file it. A “question” is a real conversation the system deliberately doesn’t try to have. The point of the label is speed of triage, not automation — the rep still reads the actual message before doing anything.

Outcome 2: defer (out-of-office)

Out-of-office is the one reply that shouldn’t stop the chain. The customer didn’t answer the quote; their mail server answered for them. Stopping the chain here would let a hot quote go cold just because the buyer was on holiday.

So the reply Lambda reads the away-until date from the auto-response (most include one) and writes a defer row to qf-reply that suppresses nudges until a couple of days after that date. If no date is present, it defers a sensible default — say five business days. The chain doesn’t reset; it simply pauses. When the customer is back, the next due nudge picks up where the cadence left off. The rep isn’t pulled in for an out-of-office, because there’s nothing for them to do yet.

Outcome 3: the expiry close-out (no reply ever comes)

Some quotes simply never get an answer. The last-call nudge went out, the chain finished, and the quote reached its expiry date with silence. The system does one tidy thing here: the daily timer writes the quote to a separate closed sheet in the same Drive folder, with the date it expired and its last-known status, and stops considering it. The closed sheet is what the monthly summary in Part 6 reports on, so “quotes that went nowhere this month” is a number the owner can actually see and learn from.

A quote that was deferred and then expired without the customer ever truly replying ends up here too, with a note — “out-of-office on 2026-06-02, never resumed” — so the trail isn’t a mystery later.

Every outcome is logged, every outcome is reversible

The qf-audit table records every reply outcome and every close-out with who (or what) caused it, the timestamp, and a snapshot of the quote’s state before and after. If the classify gets a reply wrong — a sarcastic “sure, whatever” read as an accept — the rep can run a small “reopen quote” command that restores the chain from the snapshot and lets the cadence continue. The reopen is itself an audit row, so the trail of changes stays clean.

This kind of reversibility matters because the reply is where money is on the line. A mis-sorted reply that silently kills a live deal is the one failure mode worth guarding against hardest, which is exactly why a person sees every accept, decline, and question before anything happens on it.

Next post: the cost breakdown. The whole pipeline above runs in coffee-money territory at SMB volume; Part 6 explains exactly where the dollars go.

All posts