Part 5 of 7 · Form intake router series ~5 min read

How a form submission gets confirmed

A quote request lands at 11:02pm. The customer’s browser shows “thanks, we got it” instantly. But under the hood, “confirmed” means more than one thing: the customer got a real reply, the team and the tools got the lead, nothing was sent twice, and if a tool was down, the submission is waiting safely rather than lost. This post walks through the three things that finish a submission — the customer reply, the retry budget and dead-letter queue, and the de-duplicate guard — and how the record, the deliveries, and the audit trail all stay in sync.

Key takeaways

  • Three finishing pieces: the customer reply, the retry budget with a dead-letter queue, and the de-duplicate guard.
  • The customer reply is its own queued job, so it lands even if every other delivery is slow.
  • A delivery that keeps failing lands in a dead-letter queue — held, alerted, replayable, never deleted.
  • The de-duplicate guard means a retried or double-clicked submission never sends twice.
  • Every finish writes to the audit trail, so any lead’s full story is one lookup away.

Three things that finish a submission

Three things that finish a submission A diagram showing one input on the left flowing through the dispatch step, then branching into three finishing paths. Far left: a "Routed submission" box showing the submission after dispatch — its delivery jobs already on the queue, with the customer's contact details and the routing rule attached. The three branches sit in the middle column. Branch one, Customer reply: a worker fills the auto-reply template from the rules doc with the customer's name and what happens next, then sends it via SES outbound to the customer's inbox; because this is its own queued job, the reply lands even if the team email or the CRM write is slow. Branch two, Retry budget and dead-letter: each delivery job is retried with growing backoff up to a set budget; if a downstream tool stays down past the budget, the job moves to the dead-letter queue rather than being dropped, an alert is sent, and an operator can replay it once the tool recovers — nothing is ever lost. Branch three, De-duplicate guard: before any send, the worker checks the deliveries log keyed by the submission key and the delivery type, so a retried job or a double-clicked submission never emails the team twice or writes the CRM row twice. The right side shows the convergence: every finishing action writes a row to the audit trail in DynamoDB with timestamp, submission id, action, delivery type, and result. A note at the bottom: the customer is acknowledged first and nothing is deleted — a down tool only delays a delivery, it never loses the lead. Routed submission jobs on the queue [Customer reply] [Retry budget] [De-dup guard] Path 1 Customer reply • Fill auto-reply template with name + next steps • Send via SES outbound • Own job — never blocked Path 2 Retry budget + DLQ • Retry with backoff up to the budget • Past budget → dead-letter queue, alert, replay Path 3 De-duplicate guard • Check deliveries log by key + delivery type • Already sent → skip — no double send Audit trail DynamoDB fir-audit timestamp · sub_id action · delivery result The customer is acknowledged first and nothing is deleted — a down tool only delays a delivery.
Fig 5. Three things finish a submission. The customer reply lands on its own job. The retry budget and dead-letter queue catch anything a tool refuses. The de-duplicate guard stops double sends. Every finish writes to the audit trail.

Path 1: the customer reply (the part they actually see)

The customer already got an instant “thanks, we got it” in their browser the moment they hit Send — that came straight from the intake door in Part 2. Path 1 is the proper follow-up: a real email to their inbox, filled from the auto-reply template the routing table chose for that form. The template lives in the rules doc, so the owner can word it per form — a quote-request reply might say “we’ll send pricing within one business day,” a booking reply might say “we’ll confirm your slot shortly.”

It matters that this is its own queued job. The customer’s reassurance doesn’t wait behind the CRM write or the team email. If those are slow, the customer still hears back on time. The one thing the router will never do is leave a customer wondering whether their message went anywhere — that silence is exactly what makes people fill in a form three times or give up and call a competitor.

Path 2: the retry budget and the dead-letter queue

Part 4 covered the happy retries — a tool hiccups, the job tries again, it lands. Path 2 is what happens when a tool doesn’t come back in time. Each delivery job has a retry budget: a set number of attempts over a stretch of minutes. Most outages are shorter than that and the job succeeds inside its budget. But if the CRM is down for an hour, or someone rotated an API key and nobody updated the secret, the job will exhaust its budget without landing.

When that happens, the job isn’t dropped — it’s moved to a dead-letter queue, a separate holding line for jobs that kept failing. Three things follow. An alert fires (to the admin’s email and Slack) so a human knows a delivery type is failing. The submission record is marked so you can see which leads are waiting. And once the tool is fixed, an operator replays the dead-letter queue and every held delivery goes through. The lead was never lost; it was parked safely until the world was ready for it. This is the concrete meaning of “never drops the lead” — not “nothing ever fails,” but “a failure is held and recoverable, not deleted.”

Path 3: the de-duplicate guard

Queues occasionally hand the same job out twice — it’s a normal property of systems that promise to never lose a job, and the safe trade is “at least once” rather than “exactly once.” Combined with the double-clicking customer from Part 2, that means the router has to assume any delivery might be attempted more than once. The de-duplicate guard handles it: before any send, the worker checks the fir-deliveries log for a row matching this submission key and this delivery type. If it’s already there, the work was already done — the worker records the duplicate and stops. The team never gets two copies of the same lead; the CRM never gets two rows; the customer never gets two replies.

This is the same idempotency idea from Part 2, applied to the sending side. “Do it again” safely means “do nothing if it’s already done.”

Every finish is logged, every finish is recoverable

The fir-audit table records every finishing action — reply sent, delivery succeeded, delivery dead-lettered, duplicate skipped — with the submission id, the delivery type, the timestamp, and the result. Combined with the received/checking/routed-ready status from earlier, this gives any single lead a complete story: when it arrived, why it was held or passed, where it was routed, which deliveries landed and when, and whether anything had to wait in the dead-letter queue. When a prospect calls asking “did you get my message?” the answer is a lookup, not a guess.

And because the raw submission is still in S3 and the record is still in DynamoDB, anything can be replayed. A delivery that dead-lettered, a routing rule that was wrong and got fixed, a whole batch from an hour the CRM was down — all of it can be re-driven from the saved record. The system’s memory is the only memory anyone needs.

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 and why the queue-and-retry design costs almost nothing extra.

All posts