Skip to content
theakrista.dev
Case Studies

2026 - Present

Private client work

Lead Inquiry Automation

An AI triage desk for a real-estate brokerage, every inquiry from email, Meta ads, and walk-ins classified, deduplicated, and routed to the right agent through Slack.

Senior Full-Stack DevOps Engineer · AI & workflow automation

PythonFastAPIClaude APISlack APIGmail APIMeta Graph APIGoogle Sheets APIPipedriveRender
email, Meta lead ads & walk-ins in one pipeline
3 sources
from inbox to an assigned, notified agent
Minutes
Google Sheets as the single source of truth
0 databases
classify first, then extract structured fields
2-pass AI

The Problem

A real-estate brokerage's leads arrived everywhere at once: a shared Gmail inbox mixing real inquiries with spam, Meta (Facebook/Instagram) lead forms, and walk-ins logged by hand in a spreadsheet. Every lead needed the same things (logged, deduplicated, assigned to an agent, followed up) and all of it was manual.

Assignment was the sore point. Distributing leads fairly across agents sounds trivial until you add day-off schedules, budget thresholds, sales-versus-rental tracks, referral overrides, and returning customers who must go back to their original agent, rules that lived in people's heads and got applied inconsistently.

The team's operational reality was non-negotiable: managers work in Slack and agents' records live in a Google Sheet and Pipedrive. A system that asked them to adopt a new dashboard would have failed on day one, the automation had to move into their tools, not the other way around.

The Constraints

The spreadsheet is the database

The team ran the business from a Google Sheet and wasn't going to stop. So the sheet is the single source of truth: assignments, dedup flags, CRM ids, and message timestamps all live as column values the staff can see (and fix) themselves.

Humans approve, machines prepare

An AI mis-routing a high-value buyer to the wrong agent is a business incident. Every lead is staged in a triage channel with a suggested assignee, and a manager's button click (not the model) is what commits it.

Fairness rules are business rules

Round-robin with day-off queues, budget floors and ceilings, separate rent and sales rotations, and manual-override cases (referrals, returning customers, high-value sales), the rotation logic encodes real policy and had to be exactly right, and explainable.

Spam can't reach agents, leads can't be lost

The Gmail inbox is public-facing: classification junk-filters it, dedup catches the same customer arriving twice through different channels, and returning customers merge into their existing row instead of spawning a competing lead.

The Architecture

An ingestion layer that normalizes three very different lead sources into one pipeline, a Claude classification/extraction pass that gates what gets through, and a Slack-native triage flow where rotation logic proposes and a human disposes, all persisted as plain columns in the team's own spreadsheet.

  • Classification is two-pass: Claude first decides whether an email is a real inquiry, spam, or a short-term-rental request, and only then extracts structured fields, so extraction effort (and tokens) are never spent on junk, and the classifier acts as the gate for the open inbox.
  • The round-robin engine keeps separate rent and sales rotations and advances at posting time, so consecutive leads rotate correctly even before anyone clicks approve. Agents on their day off are skipped into a FIFO queue and served first when they return.
  • Triage is fully interactive Slack: approve assigns the agent, posts to the leads channel, forwards the original email, and creates the Pipedrive lead; reject opens a reason modal; reassign updates ownership everywhere, sheet, Slack thread, email forward, and CRM.
  • All persistent state is sheet columns, assignment, dedup flags, message ids, CRM ids. The service can restart with zero data loss, and the team can read (or correct) the system's memory in the spreadsheet they already live in.

The Trade-offs

Every architecture is a set of bets. These are the ones I made, and what I gave up to make them.

Persistence

Google Sheets as the single source of truthoverA proper PostgreSQL database

Heresy on paper, correct in practice: the team already ran the business from the sheet, and a database would have made the system's state invisible to them. Idempotent writes and flag columns bought the durability that mattered, with an audit trail non-engineers can read.

Assignment authority

AI suggests, a manager's click commitsoverFully automatic assignment

The rotation engine is deterministic, but leads carry context no model sees, a VIP referral, an agent who just got slammed. Staging every lead in Slack with a pre-selected suggestion keeps the speed of automation and the judgment of a human, at the cost of one click.

Rotation policy

Strict order, advancing when a lead is postedoverLeast-assignment-count 'fairness' balancing

Count-based fairness sounds fairer and is impossible to explain when an agent asks why they were skipped. Strict order with an explicit day-off queue is predictable, auditable in one sheet row, and settles disputes by pointing at the rules.

Email ingestion

Pub/Sub push with a polling fallbackoverPolling alone, or trusting push alone

Push gives minutes-level latency; the poller catches anything push drops (watch expiry, downtime) with message-id dedup making overlap harmless. Boring redundancy, and no lead has been lost to a delivery gap.

Testing

A fully mocked offline regression suite + live tests that only DM meoverTesting against real channels and sheets

The system's failure mode is spamming real agents about fake customers. The regression suite stubs Slack, Sheets, and the Graph API so every change is verified offline; the live-fire scripts exist but can only post to my own DM.

The Outcome

The service runs the brokerage's lead operation in production, end to end, and the manual triage workflow it replaced is gone.

  • Every inquiry from email, Meta lead ads, and walk-ins now lands in one pipeline: classified, deduplicated, logged, and staged for assignment within minutes of arriving.
  • Round-robin assignment with day-off queues and business-rule overrides replaced head-kept rotation, agents get leads predictably, and disputes are settled by the sheet, not memory.
  • Managers approve, reject, or reassign leads without leaving Slack; approval cascades automatically, agent notified, email forwarded, CRM lead created.
  • Returning customers merge into their existing record with a thread reply to the original agent, instead of re-entering the queue as a competing lead.
  • Built and operated alongside the marketplace and data pipeline for the same business, the third production AI system on the same validation-first pattern.

Lessons Learned

Meet the team in their tools

The system has no UI of its own, it lives in Slack, a spreadsheet, and a CRM the team already used. Adoption was instant because nothing new had to be adopted. The best workflow automation is invisible as software.

Encode the policy, not just the mechanism

Round-robin is ten lines of code; the day-off queue, budget thresholds, and override rules are the actual system. Automating a team workflow means interviewing people about the exceptions, that's where the real spec lives.

Idempotency beats infrastructure

With dedup flags and stable ids on every write, a restart, replay, or overlapping poll changes nothing. That discipline is what makes a spreadsheet a viable database, and what makes any pipeline safe to operate alone.