A lead intake bot on AWS for a few dollars a month
A “Contact us” form gets twelve submissions this week. Three are real buyers. One of those wants a quote by Friday. Four are tire-kickers. Three are competitors fishing. One is a vendor pitching themselves because nobody answered their cold email. One is spam. By Tuesday afternoon you’ve replied to two of them and forgotten the rest. This post walks through the design of a small intake bot that catches every new lead the moment it arrives, scores it against your ideal-customer profile, pings the right rep on the hot ones with full context, and quietly parks the cold ones for nurture.
Key takeaways
- Four outside surfaces, three AWS pieces. The bot turns four lead sources into one queue.
- Every lead ends in one of four moves: hot route, warm follow-up, nurture, reject.
- The qualifier writes only from your voice and pricing files. It never invents a discount, an SLA, or a feature you don’t offer.
- Hot leads ping the right rep in Slack within seconds, with a draft reply already written.
- Runs on AWS for about $3/month at typical small-business volume.
The whole system on one page
Before any code, here’s the shape of what we’re building.
What you set up once (the outside)
- Your lead sources. The “Contact us” form on your website, your Meta Lead Ads (Facebook and Instagram lead forms), your Google Ads lead form assets, and the shared sales inbox. The bot takes webhooks from the platforms that push and polls the ones that don’t. It reads inbound email through SES. You can disconnect any source in one click if a campaign goes sideways.
- A rules folder. Three short Google Docs in a Drive folder. ICP and disqualifiers: the buyers you sell to and the ones you don’t, with the reasons for each (geography, company size, industry, role, budget). Pricing and promos: your tiers, what each one includes, the active campaign codes and their end dates, and what you can and can’t commit to in a first reply. Voice: warm, brief, never pushy. The signature line and a few opening templates per source. Edit a doc and the bot picks up the change on the next refresh. No deploy.
- A sales-team destination. The Slack channel or shared queue your team already watches. Hot leads ping right away with the original message, the score and reasons, a draft first reply, the matched rules passage, and the suggested owner. Warm and nurture leads don’t ping. They show up in the morning digest, where they belong.
- Your CRM. HubSpot, Salesforce, Pipedrive, or a Drive sheet for the smallest setups. Every lead lands here, no matter the move. Each row has the source, the score, the parsed payload, and a link to the full audit row. Nothing falls through the cracks — not even the leads the bot decides not to escalate.
What runs on every lead (the inside)
- The lead intake. Receives or polls each source. Drops duplicates by email and phone, so the same person filling out three forms in five minutes doesn’t become three rows. Screens out the obvious junk: banned-domain spam, competitor email domains, banned-phrase floods, submissions missing required fields. Writes the cleaned lead to a single queue. By the time the qualifier sees a lead, it’s in one shape regardless of source.
- The qualifier. For every queued lead, reads the message and the form fields. Pulls out three things in parallel: intent (what they’re asking for), urgency cues (deadlines, “ASAP,” named events), and fit signals (industry, company size, budget mentions). Looks up the email domain for free enrichment. Then it scores against the ICP doc and picks one of four moves. Hot route: matches the ICP and shows real intent or urgency. Warm follow-up: plausible fit, no rush; deserves a thoughtful reply. Nurture: long-tail fit, no signal; tag and park. Reject: off-ICP, competitor, vendor pitch, or spam. When it writes a first reply, it uses only the voice file and the pricing file. It never invents a discount, a deadline, or an availability promise.
- Dispatch and routing. On hot route: pings the on-call sales owner in Slack with the full package, assigns ownership in the CRM, and starts a 15-minute timer if nobody acknowledges. On warm follow-up: drops the proposed reply in the team’s draft queue with the matched pricing and ICP excerpts. A human glances at it and hits send. On nurture: tags the contact in the CRM with the right campaign and adds them to the slow-drip list. On reject: archives the lead with a reason. On every lead, regardless of move, the bot writes the full row to the CRM and appends to the 8am digest the team reads in the morning.
In plain words
A new submission lands on your “Contact us” form. The cloud reads it within a second. The email is from a real-looking domain in your ICP. The company size matches. The message says “we’re replacing our current vendor by end of June, can we get a demo this week.” The cloud pings the on-call rep in Slack with four things: the original message, a one-line summary (“hot — mid-market, urgency: end of June, demo requested”), a draft reply pulled from the voice and pricing files, and a button to claim ownership in the CRM. The rep sees it on their phone. They tap claim, glance at the draft, change one sentence, hit send. The whole loop takes under two minutes. The lead arrived at 11:47pm Saturday and would otherwise have sat in the inbox until Tuesday morning.
Total cost runs in coffee-money territory at small-business volume. Pennies per lead, dominated by a few model calls per qualified lead.
Design rules that shaped every decision
- The qualifier writes only from your voice file and your pricing file. It never invents a discount, an SLA, or a feature you don’t offer.
- Four moves, always. Hot, warm, nurture, reject. There is no fifth.
- Off-ICP and competitor leads never ping the team. They’re tagged and archived with a reason. Pings are a finite resource.
- Hot leads always include the full context: original message, score reasons, draft reply, matched ICP excerpt. The rep never has to dig.
- Configuration lives in Drive. Editing your ICP or your pricing doesn’t need a deploy.
- Every lead is written to the CRM, regardless of move. Nothing falls through.
Why this shape
Most “lead automation” tools fall into one of two traps. The first kind dumps every form submission into the team’s shared inbox, or pings every one to Slack. The team mutes the channel within a week. The third “hot lead!” ping that turns out to be a vendor pitching their lead-automation product is the moment the alerting stops working. The second kind qualifies with a static rubric (“company size > 50 = +20 points”) that can’t read the actual message. So an obvious buyer with a small website and a one-line message gets scored as cold. A tire-kicker from a Fortune 500 IP gets scored as hot. Neither is what you want at 11pm on a Saturday when a real buyer fills out your form because they finally got time to think about it after the kids went to bed.
The setup above splits the difference. Real buyers with real signal ping the on-call rep within seconds, with enough context to reply intelligently from a phone. Plausible buyers without signal become drafts the team reviews in batches; the expensive part (writing the reply) is already done. Long-tail fits get tagged and parked in nurture. Junk — vendor pitches, competitor fishing, spam — gets archived with a reason. The team never sees the junk, but you can audit it later if a real lead ever ends up there by accident.
The next four posts walk through each piece in turn: how a lead reaches the bot, how the bot reads it, how it scores and routes, and how the first-touch reply stays on policy. One diagram per post. A cost breakdown and a final engineering reference at the end.
All posts