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

How a form submission finds the right tool

The submission is checked and routed-ready. Now dispatch has to get it to the right places — the team that handles it, the CRM or sheet it belongs in, the customer who’s waiting on a reply. Get this wrong and the lead is worse than lost: it goes to the inbox nobody reads, or it vanishes because the CRM was down for ninety seconds. Four small gates sit between “routed-ready” and every delivery actually landing — and each delivery runs on its own so a slow tool can’t hold up the rest.

Key takeaways

  • The routing table maps each form to a team email, a CRM list or sheet tab, and an auto-reply template.
  • Dispatch fans the submission out into separate delivery jobs — team email, CRM write, sheet write, customer reply.
  • Each delivery is its own job on an SQS queue with retries and growing backoff.
  • A slow CRM never delays the customer reply — the jobs are independent.
  • Every delivery is logged so the next step knows it landed and won’t double-send.

Four gates between routed-ready and delivered

Four gates between a routed-ready submission and every delivery landing A horizontal flow diagram. On the far left, a "Routed-ready" box: the checker handed over a submission with its form_id, its category, and the customer's details. Four gates sit in a row to the right, each drawn as a vertical bar. Gate 1: Resolve route — looks up the routing rule for the form_id in the routing table mirrored from the Drive sheet; returns the team email, the CRM list or sheet tab, and the auto-reply template to use. Gate 2: Fan out — turns the one submission into several independent delivery jobs: email the team, write to the CRM, append to the sheet, and send the customer auto-reply; each becomes its own message on the SQS queue so they run in parallel and don't block each other. Gate 3: Queue and retry — each delivery job is processed by a worker Lambda; if a downstream tool returns an error or times out, the job is retried with growing backoff, and a slow CRM never delays the customer reply because that's a separate job. Gate 4: Confirm and log — on a successful delivery, the worker records it against the submission in DynamoDB so the same delivery is never sent twice and the next step in Part 5 can confirm everything landed. After all four gates, the deliveries land: the team inbox via SES outbound, the CRM via its API, the sheet via the Sheets API, and the customer's inbox via SES outbound. A note at the bottom: each delivery is independent and retried — one tool being down never costs a lead or holds up the others. Routed-ready form_id, category, customer Gate 1 Resolve route look up form_id in routing table team + CRM + reply from the Drive sheet, no deploy Gate 2 Fan out one submission becomes jobs email, CRM, sheet, reply onto SQS Gate 3 Queue + retry worker per job if a tool errors, retry with growing backoff, jobs independent Gate 4 Confirm + log record each delivery done never sent twice, Part 5 confirms all Deliveries land — team inbox, CRM, sheet, and customer reply SES outbound for email · CRM API · Sheets API for the sheet every delivery logged to DDB fir-deliveries — the worker won’t double-send Each delivery is independent and retried — one tool being down never costs a lead or blocks the others.
Fig 4. Four gates between routed-ready and delivered. Resolve the route. Fan out into separate jobs. Queue each one with retries. Confirm and log. Then the deliveries land — team, CRM, sheet, and customer — without any one slow tool holding up the rest.

Gate 1: resolve the route

Dispatch starts by looking up the routing rule for the submission’s form_id (and category, for the catch-all contact form) in the routing table. The table is a Google Sheet your team can edit, mirrored to S3 every fifteen minutes by a small sheet-sync Lambda — so dispatch reads a fast local copy, never the live sheet, on every submission. Each row says four things: which team email gets it, which CRM list or sheet tab the row goes into, which auto-reply template the customer receives, and an optional priority flag for forms that should also page someone.

Because the table is a sheet, the day sales splits into “new business” and “renewals,” or you add a careers form, the owner edits a row and the next submission routes the new way. No code change, no deploy, no waiting on an engineer.

Gate 2: fan out into independent jobs

This is the gate that protects you from slow tools. Instead of doing all the deliveries one after another in a single function — where a slow CRM would delay the team email and the customer reply behind it — dispatch turns the one submission into several separate jobs and drops each onto an SQS queue. A queue is just a managed waiting line for jobs. There’s a job to email the team, a job to write the CRM, a job to append the sheet row, and a job to send the customer’s auto-reply. Each is picked up and run on its own.

Independence is the whole benefit. The customer’s “we got it” reply doesn’t wait behind the CRM. The team email doesn’t wait behind the sheet. If three deliveries succeed instantly and one is slow, three land instantly and the fourth catches up on its own clock.

Gate 3: queue and retry

Each job is processed by a small worker Lambda that does exactly one delivery. If the downstream tool answers cleanly, the job is done. If it returns an error or times out — the CRM is mid-deploy, the email service hiccupped, the sheet API is briefly rate-limited — the job isn’t lost. SQS hands it back after a wait and the worker tries again, with the gap growing each time (a few seconds, then tens of seconds, then minutes). Most transient failures clear within a try or two, and the delivery lands without anyone noticing there was a problem.

This is the heart of “never drops the lead.” A normal form-handler that emails inline has exactly one chance: if the send fails, the lead is gone. A queued job has many chances spread over time, and the submission sits safely in storage the whole while. What happens if a tool stays down past every retry is the subject of Part 5 — the dead-letter queue — but the short version is that even then, nothing is deleted.

Gate 4: confirm and log

When a delivery succeeds, the worker writes a row to the fir-deliveries table in DynamoDB recording which submission, which delivery (team_email, crm, sheet, customer_reply), and when. Two things fall out of this. First, the system can prove a lead was delivered — the audit answer to “did sales actually get this?” is a lookup, not a shrug. Second, because each job checks the log before acting, a job that gets retried after it had actually already succeeded (which queues occasionally do) won’t send the team a second copy or write the CRM row twice. The delivery is recorded as done, so the retry is a no-op.

Why the gates exist

None of these gates are exotic. They’re the care a thoughtful person would take by hand — look up where this should go, do each errand separately so a slow one doesn’t hold up the rest, try again if a delivery bounces, and write down what you’ve done so you don’t do it twice. Putting them in code as four small steps makes them part of the design, not a feature you’re trusting a fragile inline send to get right under pressure.

Next post: how a submission gets confirmed end to end — the customer reply, the de-duplicate guard, the retry budget, and the dead-letter queue that catches anything a downstream tool simply refuses to accept.

All posts