Home Should I file? Storm damage Verify a roofer Services About Contact Call (469) 555-0142 Get an estimate

Internal architecture · Proposal artifact

The system, end to end.

Most freelancers will pitch you a website. This is the system that website is the surface of: NWS auto-publish, source-tagged URL attribution, a 30-field GHL schema, a 15-stage pipeline, a lead-scoring engine, a Whisper-and-Sonnet call-recording loop, and the owner dashboard that ties them together. Read this in 10 minutes, then we'll get on a call to walk it.

Astro 5 + Tailwind v4 + CF Pages GHL v2 API Cloudflare Workers AI (Whisper) Anthropic Sonnet 4.6 Iowa Mesonet NWS feed

Diagram

Every node, every protocol.

The split that makes this defensible: GHL owns marketing automation + contact + pipeline; AccuLynx owns production (measurements, supplements, crews); CF Pages + Workers own routing, attribution, storm intelligence, and recordings.

                          ┌──────────────────────────────────────────┐
                          │   ATTRIBUTION SOURCES                    │
                          │  - Outbound AI call (VAPI/Bland/Synthflow│
                          │  - Google/Meta Ads UTMs                  │
                          │  - Door knock QR code                    │
                          │  - Yard sign QR (storm-area canvass)     │
                          │  - Referral link (?ref={leadId})         │
                          │  - Organic SEO                           │
                          └─────────────────┬────────────────────────┘
                                            ▼ HTTPS GET (cookie + LS write)
┌──────────────────────────────┐    ┌──────────────────────────────────────────┐
│ NWS / IEM Storm Feed         │    │  PUBLIC SITE  (Astro 5 + Tailwind v4)    │
│  cron */15min CF Worker      │    │  CF Pages   linecrewroofing.com          │
│  → poll lsr.geojson?wfo=FWD  │    │  - / (homepage)                          │
│  → filter HAIL ≥ 1.5″        │    │  - /storms/{slug} (auto-generated)       │
│  → write to KV: storm:events │    │  - /should-i-file (calculator)           │
│  → notify owner Telegram     │    │  - /verify-a-roofer (skeptic pre-read)   │
│  → draft Astro MDX page      │    │  - /h?c={callId} (handoff personalized)  │
│    (PR to GitHub auto)       │    │  - /api/lead   (POST form intake)        │
└──────────────────────────────┘    └─────────┬────────────────────┬───────────┘
                                              ▼                    ▼
                                  POST JSON ┌────────────────────────┐
                                            │ CF Pages Function:     │
                                            │  /api/lead             │
                                            │  - Turnstile verify    │
                                            │  - rate-limit (KV)     │
                                            │  - score lead (0-100)  │
                                            │  - parse first-touch   │
                                            │  - fan out 3 ways:     │
                                            │    1) GHL upsert       │
                                            │    2) Sheets mirror    │
                                            │    3) Slack/Telegram   │
                                            │  - return 200 fast     │
                                            └─┬────┬──────┬──────────┘
                                              ▼    ▼      ▼
                                ┌───────────────┐ ┌─────────┐ ┌─────────────┐
                                │ GoHighLevel   │ │ Google  │ │ Owner pings │
                                │ v2 API        │ │ Sheets  │ │ via Telegram│
                                │ - upsert      │ │ mirror  │ │ if score≥80 │
                                │ - 30 fields   │ │ (safety │ └─────────────┘
                                │ - tag storm   │ │  net)   │
                                │ - assign stage│ └─────────┘
                                └───────┬───────┘
                                        ▼ workflow on stage change
                          ┌─────────────────────────────────┐
                          │ GHL WORKFLOW ENGINE             │
                          │  - SMS via LeadConnector        │
                          │  - Email via Resend             │
                          │  - Cal.com booking sync         │
                          │  - VAPI inbound webhook         │
                          │  - Stale-opportunity nudges     │
                          │  - 15-stage pipeline routing    │
                          └─────────────────────────────────┘
                                        │
                            (post-walk) ▼
                          ┌─────────────────────────────────┐
                          │ HANDOFF TO PRODUCTION           │
                          │  AccuLynx job creation          │
                          │  CompanyCam photo sync          │
                          │  Hourly cron: AccuLynx → GHL    │
                          └─────────────────────────────────┘
                                        │
                                        ▼
                          ┌─────────────────────────────────┐
                          │ OWNER DASHBOARD (/admin)        │
                          │  CF Access auth                 │
                          │  Reads Supabase + GHL API       │
                          │  Funnel · Sources · Capacity    │
                          │  Recordings · Hot queue · AR    │
                          └─────────────────────────────────┘

Source attribution

Every URL carries its origin.

Every entry point gets its own URL signature. The page reads the params, personalizes the copy, and writes them to the GHL contact when the form submits - so every lead's true origin is recoverable, not guessed.

// Sample URL: AI-call follow-up SMS link

https://linecrewroofing.com/storms/plano-2026-04-26-hail
  ?s=ai_call  // source
  &c=plano-2026-04-26-hail  // campaign / storm
  &ag=ai-emma  // agent that called
  &n=willow-bend  // neighborhood
  &fn=TWFyaWE=  // first name (base64)
  &ph=KzEyMTQ1NTUwMTIx  // phone E.164 (base64)

// On page load, attr.js writes to localStorage + cookie + form hidden fields

> localStorage.lc_ft = { params: {...}, landed_at, landing_url, referrer, user_agent }

GHL custom fields schema

30 fields. 12 ship Phase 1.

Every field has a defined source, type, and downstream use. Phase 1 (12 fields) ships with the demo and covers attribution, scoring, compliance. Phase 2 (the rest) ships during the build engagement and adds production sync + carrier intelligence + call summaries.

Field name Type Description Phase
lead_source dropdown ai_call · sms · doorhanger · ad_meta · ad_google · referral · organic · qr P1
lead_source_detail text utm_source + utm_campaign concatenated P1
first_touch_url url Full landing URL with query params, for forensics + path replay P1
first_touch_at datetime ISO timestamp of first site visit, for velocity calculation P1
first_touch_campaign text Campaign / storm event ID at first touch P1
storm_event_id text Slug, e.g. "plano-2026-04-26-hail" - joins to storm record + dashboard P1
outreach_agent text AI caller ID (vapi_b2x9) or human crew lead - leaderboard P1
address_zip text 5-digit zip - crew routing, storm match, density score P1
lead_score number 0-100 weighted score, computed at form-submit P1
lead_score_breakdown text JSON Audit trail of how the score was reached P1
consent_sms boolean TCPA-compliant SMS consent, required for automated messaging P1
consent_calls boolean TCPA-compliant call consent P1
neighborhood_first_touch text willow-bend, prestonwood, etc. Drives copy + crew assignment P2
storm_hail_size_in number Hail size in inches at landing-page event P2
storm_event_date date Date of NWS-confirmed storm P2
roof_age_estimate number Years; pulled from form OR DCAD/TAD lookup P2
insurance_carrier dropdown State Farm · Allstate · USAA · Farmers · Liberty Mutual · TX Farm Bureau · Other P2
claim_status dropdown none · planning · filed · adjuster_scheduled · approved · denied · supplementing · paid P2
claim_number text Carrier claim number, set after filing P2
quoted_amount currency Synced from AccuLynx hourly cron P2
signed_amount currency Final contract value P2
install_scheduled_date date Synced from AccuLynx P2
install_completed_date date Triggers review-request automation P2
crew_assigned text Lead crew member, drives SMS personalization P2
referral_source_lead_id text GHL contactId of referrer - for attribution + payout P2
last_call_recording_url url R2 signed URL to most recent call audio P2
last_call_summary text 4-line LLM summary of most recent call P2
last_call_sentiment number -1.0 to 1.0, gates hot-lead routing P2
time_in_current_stage_hrs number For SLA escalation rules P2
tcpa_optout boolean Suppresses all automated messaging when STOP received P1

Pipeline stages

15 stages, each with an SLA.

Pipeline name in GHL: Storm Response - DFW. Workflows hook on the Pipeline Stage Changed trigger. Stale-opportunity SLAs auto-escalate.

  1. 1 New Lead SLA: 5 min SMS auto-reply within 60s; owner SMS direct if score ≥80
  2. 2 Tried-to-Reach SLA: 48 hr 3-touch cadence: SMS +30min, email +2h, SMS +24h; drop to nurture if no response
  3. 3 Qualified - Walk Pending SLA: 24 hr Cal.com link sent; manual call task if no booking in 6h
  4. 4 Roof Walk Scheduled SLA: until time Reminder SMS at -24h, -2h, -15min; weather check for reschedule
  5. 5 Walk Done - Quoting SLA: 48 hr AccuLynx job created; quote due nudge if 48h slip
  6. 6 Quoted SLA: 14 days Email/SMS quote follow-ups at +1d, +3d, +7d
  7. 7 Claim Filed SLA: 14-30 days Insurance liaison playbook; weekly check-in
  8. 8 Claim Approved SLA: 7 days Send contract via PandaDoc; deposit invoice
  9. 9 Contract Signed SLA: 7 days Stripe deposit invoice; install scheduling task
  10. 10 Install Scheduled SLA: until install Day-before reminder; crew dispatch SMS; weather check
  11. 11 Install Complete SLA: 3 days Final invoice; thank-you SMS + photo recap
  12. 12 Final Invoice Sent SLA: 30 days Payment-due drip; AR follow-up workflow
  13. 13 Paid + Review Requested SLA: 30 days Google review SMS, NPS, referral request with ?ref={contactId}
  14. 14 Customer / Referral Network SLA: ongoing Quarterly check-in; reactivation on new storm in zip
  15. 15 Drip Nurture (parking lot) SLA: - Monthly value-add email; reactivate on new storm in zip

Lead scoring

0-100 score at form-submit, drives routing.

score = clamp(0..100,
  +25 if storm_event_id present AND event in last 21 days
  +15 if hail_size_in >= 1.75 in lead's zip in last 30 days
  +10 if address_zip in DFW priority list
  +10 if roof_age_estimate >= 12
  +10 if insurance_carrier in [State Farm, Allstate, USAA, Farmers]
  +15 if lead_source = ai_call AND VAPI sentiment >= 0.6
  +10 if referral_source_lead_id is set
  + 5 if consent_sms AND consent_calls
  + 5 if form_completion_seconds < 90  // human, not bot, not abandoner
  -20 if address_zip not served (out of DFW)
  -10 if hours_since_first_touch > 168
)

Routing:
  >=80  →  owner SMS direct + ring nearest crew + tag "hot"
  50-79  →  standard pipeline + automated nurture
  <50   →  drip nurture only, no owner ping

Call recording → CRM loop

Every call summarized in 4 lines, automatically.

After every inbound or outbound call, the recording goes through Whisper for transcription and Sonnet for a 4-line summary that lands in the GHL contact's notes. Prep time before callbacks drops from 4 minutes to 20 seconds. Compounds across hundreds of calls per month.

  1. 1. Twilio recordingStatusCallback fires when the call ends.
  2. 2. CF Worker validates Twilio HMAC signature, fetches the WAV via signed URL, streams into Cloudflare R2.
  3. 3. Cloudflare Workers AI: whisper-large-v3-turbo transcribes (~11k neurons per minute of audio).
  4. 4. Anthropic Sonnet 4.6 summarizes: 4 lines + sentiment_score (-1..1) + flags ["mentions_competitor", "asked_for_callback", "ready_to_book"].
  5. 5. PATCH the GHL contact: append note, set last_call_summary, last_call_sentiment, last_call_recording_url.
  6. 6. If sentiment < 0.3 OR ["complaint", "competitor_chosen"] flagged → Telegram alert to owner with playback link.

Average end-to-end latency: 18 seconds post-call. Cost at 500 calls/month × 4min/call ≈ $0 (within Cloudflare's $5/mo Workers AI tier) + ~$2 of Anthropic API.

NWS storm-event auto-publish

From NWS confirmation to live landing page in ~30 minutes.

Most DFW competitors react to storms a day later. This pipeline drafts the storm landing page within minutes of the NWS Local Storm Report being published - owner reviews on phone, taps Approve, page is live before the chaser convoy hits the affected zips.

[CF Cron Trigger every 15 min]
   ↓
[fetch NWS LSR feed for FWD WFO + DFW counties]
   https://mesonet.agron.iastate.edu/cgi-bin/request/gis/lsr.py
     ?wfo=FWD&recent=3600&fmt=json
   ↓
[filter: typetext=HAIL, magf>=1.5,
         county IN (Collin, Dallas, Denton, Tarrant, Rockwall)]
   ↓
[match: county+date against existing src/content/storms/*.mdx]
   ↓
[if no match: draft new storm record]
   - generate slug: {city}-{date}-hail
   - extract neighborhoods from polygon overlap with US Census TIGER ZCTA
   - draft copy via Claude API (LSR text + city + hail size)
   - POST to D1: storms_drafts table, status=pending_review
   ↓
[Telegram alert to owner: "⚡ New hail event in Plano, 2.25in. Approve to publish?"]
   ↓
[Owner taps "Publish" → GitHub commit + CF Pages deploy → live in 90s]
   ↓
[Bonus: GHL workflow auto-fires AI-call list filtered to ZIPs in storm polygon]

Phased delivery

Three phases. Each shippable independently.

Phase 1 - Demo (now)

  • · Astro site core, all 12 pages
  • · /api/lead Pages Function (Turnstile + GHL upsert + Sheets mirror)
  • · 12 GHL Phase-1 custom fields + 5 pipeline stages
  • · Filing Calculator, Verify-a-Roofer, Priority List, /vault, /who-called, /h, /admin mock
  • · Source-tag URL JS + first-touch attribution
  • · One sample storm landing page (Plano 2026-04-26-hail) demonstrating the pattern

Target: ~6-8 hours of build. Ships before the proposal call.

Phase 2 - Build (week 2-4)

  • · Full 30-field GHL schema + 15-stage pipeline + every workflow
  • · Twilio call-recording → Whisper → Sonnet pipeline live
  • · VAPI/Bland/Synthflow handoff fully integrated
  • · Lead-scoring engine + routing logic deployed
  • · Owner dashboard polished (all 9 widgets, real data)
  • · AccuLynx bidirectional sync (one-way push GHL→prod, hourly pull prod→GHL)
  • · TCPA consent capture + audit log

Target: 80-120 hours engagement.

Phase 3 - Compounding (month 2+)

  • · NWS auto-publish wiring (the showpiece)
  • · Drone-footage hosting via Cloudflare Stream
  • · Real Pre-Storm Photo Vault (Nearmap or EagleView paid imagery)
  • · Did-You-Get-Called live data via Robokiller/Truecaller commercial agreement
  • · Carrier-adaptive form (6 conditional experiences)
  • · Adjuster Co-Pilot homeowner portal
  • · Anniversary drone re-flyover automation
  • · Internal Adjuster Heatmap (per-adjuster approval rate intelligence)

Compounding moat. Quote per feature.

Compliance baked in

Texas-specific legal layer, on every form and contract flow.

Most freelancers don't know these laws exist. The site handles each correctly - both as a credibility signal and as a competitive moat.

  • HB 2102 deductible disclosure

    Effective 2019. Every quote PDF + LP form footer states the homeowner must pay full deductible. Class B misdemeanor to waive.

  • Ch.601 / Ch.27 (RCLA) 3-day cancellation

    Door-signed contracts get a printable Notice of Cancellation in 10pt boldface per BC §601.052.

  • Public-adjuster prohibition (TX § 4102.163)

    Site copy never says "we negotiate with your insurance." Instead: "we document, you stay in control of the claim."

  • TCPA SMS consent

    Form has separate unchecked checkbox; consent record stored with timestamp + IP + user-agent.

  • STOP/HELP handling

    GHL native, plus tcpa_optout flag suppresses all future automated messaging across all storm events.

  • Door-to-door permits (Plano, Frisco, McKinney)

    Doorhangers list permit number. Crew member name visible on storm-LP for permit cross-check.

  • TDI Fraud Unit alignment

    Site links to FraudUnit@tdi.texas.gov from the Verify-a-Roofer page. We send homeowners to authority.

  • RCAT voluntary cert reframe

    Homepage explains TX has no state license. Honest framing outperforms vague "licensed and insured."