Skip to content

Goal

Real estate leads go cold in minutes: whoever replies first usually gets the showing. Build a system that answers every inquiry immediately (text + email, from the assigned agent, with similar listings) and keeps following up for a week without anyone remembering to.

Prerequisites

  • Modules 05 (Gmail, follow-ups), 13 (static data), 15 (injected clock), 16 (continue-on-error reports)
  • For the live part: Gmail, and a Twilio number for SMS

Concepts

  • One workflow, many entry points. New leads, the drip scheduler, stop requests and status checks are separate triggers in one workflow. All of them pass through Settings, and a Switch routes by which trigger ran ($('Drip Run API').isExecuted). They share one registry in workflow static data, which is only possible inside a single workflow.
  • Parse anything. Leads arrive as clean JSON (website form) or as a portal notification email. The parser reads labeled lines (Name:, Email:, Phone:, Property:, Listing ID:), falls back to regexes for any email/phone/street address, detects the portal from the sender/subject, and infers intent (rent/lease → renter, "home worth"/"selling" → seller).
  • Scheduler instead of long waits. A 7-day Wait node is fragile to edit and hard to test. Instead, each lead stores its drip steps with due_at dates, and an hourly job sends whatever is due. Injecting now makes the whole schedule testable in seconds.
  • Round robin. A counter in static data assigns new leads to agents in turn; duplicates keep their agent.
  • Stop is part of the design. Every drip must end when the lead replies, books or opts out. The stop endpoint is what your inbox/SMS/CRM automation calls (Exercise 2).
  • Compliance. Texting leads is regulated (e.g. TCPA in the US, plus carrier rules for business texting). Only text people who gave consent (portal inquiries usually include it, but check), identify yourself, include opt-out wording, and honor STOP immediately. This is not legal advice; check your market's rules.

Build it

Step 1: Triggers, Settings, Route

  • Triggers: Webhook New Lead API (POST re/lead), Gmail Trigger Portal Lead Emails (Simplify off, query from:(zillow.com OR realtor.com OR homes.com OR redfin.com) newer_than:1d, unread), Schedule Drip Every Hour, Webhook Drip Run API (POST re/drip/run), Webhook Stop Lead API (POST re/lead/stop), Webhook Lead Status API (GET re/lead/status).
  • Edit Fields Settings (edit me): brokerage, booking link, agents (name/email/phone), sms_from, sms_enabled, dedupe_days, drip steps, and demo listings (id, address, city, price, beds, baths, type sale/rent, status, listed_at, url).
  • Switch Route with four renamed outputs: new lead, drip, stop, status.

Step 2: New lead lane

  1. Code Parse Lead (workflow/code/parse.js) → IF Has Contact? (email or phone) → 400 otherwise.
  2. Code Match Listings (match.js): the asked listing by id or address; similar = same type, same city, active, price within ±20%, closest first, max 3 (none for sellers).
  3. Code Register Lead (register.js): dedupe key = email or phone; new → next agent (round robin) + drip schedule; duplicate → same agent, same schedule, inquiries + 1. Skipped on dry runs.
  4. Code Compose Replies (compose.js): SMS ≤ 320 chars ending with "Reply STOP to opt out."; email with similar homes and a pre-approval nudge for buyers who aren't; separate seller and duplicate wording.
  5. IF Dry Run? → Respond plan. Else IF Text Them? (phone and sms_enabled) → Twilio SMS → Send (from sms_from) → Gmail Send (sender name = agent); both On Error → Continue.
  6. Code Lead Report (SMS ok if a sid came back, email ok if an id did) → IF From API? → Respond; plus Alert Agent (Exercise 1).

Step 3: Drip lane

  1. Code Find Due Messages (due.js), Always Output Data on: for active leads, every unsent step with due_at <= now, rendered from three templates (check-in, listings, last chance).
  2. IF Send Now? (has a key and not dry run) → Gmail Send Drip Email (On Error → Continue).
  3. Code Record Drip Results (record-drip.js): set sent_at on success, last_error on failure (retried next run) → respond if called via API.

Step 4: Stop and status lanes

Code Stop Drip (stop.js) sets status: stopped and lists the cancelled steps (404 if unknown). Code Lead Status (status.js) returns the record.

Publish (static data only persists for production executions).

Test it

./test/test.sh                                                      # 30 checks, no credentials
WITH_CREDS=1 TEST_EMAIL=you@example.com TEST_PHONE=+1... ./test/test.sh   # real SMS + email

Watch the drip without waiting a week:

curl -X POST http://localhost:5678/webhook/re/drip/run -H 'Content-Type: application/json' \
  -d '{"now":"2026-12-01T10:00:00Z","dry_run":true}'

Common problems

  • Parser misses fields from a real portal email: open the Gmail trigger output and adjust the labels in parse.js (field('Name')…) to that portal's layout.
  • Every lead goes to the same agent: you're testing with dry runs (they don't advance the counter) or running manually (static data isn't saved).
  • Drip sends twice: two schedules running (e.g. a duplicate workflow), or sent_at not saved because the run was manual.
  • SMS fails with a Twilio error about the sender: sms_from must be a number on your Twilio account, and US business texting may require registration (10DLC) first.
  • Leads keep getting emails after replying: nothing calls the stop endpoint yet (Exercise 2).