Skip to content
Muhammad Siraj
All work

Storm-triggered lead engine

StormLead

Four autonomous LangGraph pipelines that turn storms across 10 metros into outreach, replies and booked inspections, with a person approving every email before it sends.

The StormLead overview dashboard. “2 homeowners contacted after storms in 1 city. 2 inspections booked.” A severe-weather card for Dallas, TX, and a funnel from outreach sent to booked.

Problem

After a storm, the roofing companies that reach affected customers first get the work. Doing that by hand means watching the weather in many cities, writing outreach to the right customers, keeping up with replies and booking inspections before the moment passes.

Constraints

What made it hard.

  • Outreach goes to real customers in the company’s name, so no email can send without a person approving it.
  • A pipeline paused for approval must survive a closed terminal or a restart and resume exactly where it stopped.
  • Four stages run on their own schedules, so every handoff has to be unambiguous without any stage calling another.
  • Only opted-in customers may ever be contacted.
  • Operators need to see everything the agents do without being able to change it.

Solution

StormLead is four independent LangGraph pipelines that share one Postgres database as their coordination point, one scheduler that runs them unattended, and a read-only operations dashboard over the whole thing.

Autonomy stops where the company’s reputation starts. Every outreach email waits at a human-review gate built with LangGraph’s interrupt() and Postgres checkpointing. It is the one deliberate manual step in an otherwise automated pipeline.

How StormLead works
  • Model output
  • Deterministic code
  • Human decision
  • External system or data

Lead find, daily at 08:00

  1. System: Check storms per cityCustom OpenWeather MCP server
  2. Code: Find opted-in customers
  3. Model output: Draft personalised outreach
  4. Human decision: Approve, reject or rewriteinterrupt(), checkpointed
  5. System: Send emailGmail MCP server

Reply triage, every 30 min

  1. System: Read new Gmail replies
  2. Model output: Classify the replyWith a one-sentence rationale
  3. Code: Send a Calendly linkComposio Tool Router

Booking notify, hourly

  1. System: Check Calendly for bookings
  2. System: Alert the team in Slack

Close stale, daily at 08:30

  1. Code: Close leads silent for 7 days

Stages hand off only by writing a status to the tracking table in PostgreSQL. Simplified diagram.

The read-only operations dashboard shows the whole system from the same Postgres database the agents write to.

StormLead pipeline view: four stage cards (Lead Find daily 08:00, Reply Triage every 30 min, Booking Notify hourly, Close Stale daily 08:30) and the status each one writes, above a board with one column per status. Two leads sit in Booked.(open full size)
Pipeline: four agents that hand off only by writing a status, and one column per status. The handoff diagram, made literal.
Cities and weather view: ten service-area cities with opted-in customer counts. Dallas, TX is marked severe with a thunderstorm with heavy rain and 45 mph wind, and 2 booked leads; the other cities have no storm data.(open full size)
Cities and weather: the 10 metros in the service area. A city is highlighted by the same rule Lead Find uses to decide it qualifies for outreach.
Leads table with two booked leads in Dallas, TX, each showing the storm that triggered outreach and the model's one-sentence triage rationale. Email addresses are blacked out.(open full size)
Leads: every outreach record with the storm behind it and the model’s triage rationale. Email addresses redacted.
Bookings view: two booked inspections with their Calendly appointment times and lead numbers, each marked severe, and a note that each booking was announced in the leads-only Slack channel. Email addresses are blacked out.(open full size)
Bookings: inspections detected on Calendly and announced in Slack. Email addresses redacted.
Review queue view titled Human review gate, explaining that Lead Find never sends without a person approving it, showing that nothing is waiting, and the terminal commands to start a run and resolve waiting drafts.(open full size)
The human review gate. Runs paused at interrupt() are listed here, and a person resolves them from the terminal.

The dashboard has six views in all: overview, leads, pipeline, cities and weather, bookings, and the review queue. Light and dark themes are both built in.

Technology and why

LangGraph
interrupt() pauses a run at the human-review node, and the Postgres checkpointer lets it resume later from the same state.
MCP
A custom stdio MCP server wraps OpenWeather, and hosted Klavis MCP servers handle Gmail and Slack.
Composio
Calendly is reached through Composio’s Tool Router, driven by a small ReAct agent with a 90-second timeout and a 12-step limit so a stuck call fails fast.
OpenAI
Drafts and rewrites outreach, classifies replies and runs the Calendly tool-router agent.
PostgreSQL
Holds customers, the tracking table whose status column is the handoff between stages, and every LangGraph checkpoint.
React and FastAPI
The operations dashboard. Its connection pool sets default_transaction_read_only, so Postgres itself refuses any write.
LangSmith
Traces every graph run, so drafts and classifications can be inspected after the fact.

Hard problems

Human approval in an autonomous system

After drafting, the graph hits interrupt() and genuinely pauses, checkpointed to Postgres. A person approves, rejects or asks for a rewrite with feedback for every draft. Scheduled runs that pause are logged to a pending-review table and resolved later with one command.

Handoffs without tight coupling

Stages never call each other. Each writes a status (awaiting reply, interested, booked, no response) and the next stage polls for it, so each runs on its own cadence without holding state about the others.

A dashboard that cannot break anything

The dashboard is read-only by construction. Postgres refuses writes from its connection pool, so the safety does not depend on the UI behaving.

Long-running process, one set of connections

The scheduler builds the MCP client, every tool and the checkpointer once at startup and reuses them, rather than opening a new Calendly session and database connection on every 30-minute tick.

Results

Autonomous LangGraph pipelines
4
Metros monitored for storms
10
Views in the read-only ops dashboard
6
Seeded customers in end-to-end testing
400

Model and evaluation

Model
OpenAI models for drafting, rewriting, reply classification and the Calendly tool-router agent.
Evaluation
Tested end to end on 400 seeded customers, with every graph run traced in LangSmith.