Live Automation

The Workflow Behind the Pitch

A complete walkthrough of the GTM pipeline I built for Ambient.ai—from initial research to live outreach.

254
Districts Scraped
14
Automation Steps
2
Live Campaigns
Scroll to explore
Phase 1

Strategy & Research

Before writing a single line of code, I researched the market, defined the ICP, and selected target personas.

01
Research

Market Research & Gap Analysis

Understand the K-12 security landscape, regulatory drivers, and competitive gaps before building anything.

Key Insights Discovered

  • Alyssa's Law — Texas deadline 2025-2026 creates urgency
  • Antioch shooting (Jan 2025) — Recent incident = active buying
  • Competitor gap — Verkada/Rhombus detect visible weapons only
  • Ambient's edge — Behavioral detection catches threats earlier
  • Budget cycles — Districts allocate security spend in Q1-Q2

Market Opportunity

  • 1,200+ Texas school districts
  • $3B+ annual security spend in K-12
  • Post-Uvalde: 73% of districts reviewing security tech
  • AI security adoption up 340% YoY
Web Research News Analysis Competitor Intel
02
Strategy

Define Ideal Customer Profile

Not all districts are equal. Define criteria for districts most likely to buy, buy fast, and have budget.

icp_criteria.yaml
ideal_customer_profile: enrollment: min: 10000 # Below = low ACV, not worth it max: 50000 # Above = 12+ month sales cycles sweet_spot: "10K-50K" # Big budget, fast decisions geography: state: "Texas" # Alyssa's Law deadline priority_regions: - "DFW suburbs" # Rapid growth = new $$$ - "Austin corridor" # Tech-forward buyers - "San Antonio ring" # Military families = safety focus signals: - "Recent bond passed" # Has budget allocated - "New superintendent" # Open to new vendors - "Has safety director" # Champion exists
TierEnrollmentCountRationaleAction
Sweet Spot10K-50K98Fast decisions, real budgetTarget
Enterprise50K+1812+ month cycles, RFP hellExclude
Mid-size5K-10K61May have budget constraintsSecondary
Small<5K77Low ACV, not scalableExclude
03
Strategy

Select Target Personas

Identify decision makers and champions. Plan dual-thread outreach when multiple personas exist.

personas.yaml
target_personas: - title: "Superintendent" priority: 1 role: "Budget authority, final decision maker" pain_points: - "Board pressure after incidents" - "Compliance deadlines (Alyssa's Law)" - "Parent/community concerns" messaging: "ROI + compliance + community trust" - title: "Director of Safety / Security" priority: 2 role: "Technical champion, daily operator" pain_points: - "Current tools only detect visible threats" - "Too many false alarms" - "Integration headaches" messaging: "Product capabilities + peer validation" outreach_strategy: "dual_thread" # When Safety Director exists, contact BOTH personas # Creates internal champion + executive sponsor
Persona Mapping Dual-Thread Strategy Message Matching
Phase 2

Data Collection & Enrichment

Now that I know WHO to target, build the data pipeline to find them.

04
Data Collection

Scrape Texas Districts

Python scraper extracts all 254 Texas school districts with website domains. ICP filters applied during scrape.

get_all_texas_districts_domains.py View Code
class TexasDistrictScraper: def run(self, icp_filter=True): # Source 1: Texas Tribune API districts = self.scrape_tribune() # Source 2: Wikipedia backup districts += self.scrape_wikipedia() # Find domains via pattern matching for d in districts: d['domain'] = self.find_domain(d['name']) # Apply ICP filter (10K-50K enrollment) if icp_filter: districts = [d for d in districts if 10000 <= d['enrollment'] <= 50000] return districts # 98 ICP matches
Python BeautifulSoup Pandas
05
Enrichment

Clay API Enrichment PAID

Find the personas I defined at each district. Get verified emails, phones, and LinkedIn profiles.

clay_enrichment.py View Code
# Search for personas I defined in Step 3 TARGET_TITLES = [ "Superintendent", "Director of Safety", "Chief of Security", "Chief of Police", ] def enrich_district(domain): people = clay.find_people( domain=domain, titles=TARGET_TITLES ) for person in people: person['email'] = clay.find_email(person) person['linkedin'] = clay.find_linkedin(person) person['persona'] = classify_persona(person['title']) return people
Clay API People Search Email Finder
05b
Alternative

Free Data Alternative FREE

Zero-cost alternative to Clay enrichment:

Result: Scraped 9,738 contacts using this method (before filtering for our 98 target districts)
Download 9,738 Contacts (FREE)
Texas TEA Webhound.ai Web Scraping
06
Prioritization

Score & Prioritize Leads

Not all leads are equal. Score based on buying signals to focus effort on highest-potential opportunities.

score_leads.py
# Lead Scoring Model SCORING_SIGNALS = { 'has_safety_director': +15, # Champion exists! 'recent_security_incident': +12, # Urgency driver 'new_superintendent': +10, # Open to change 'passed_bond_measure': +8, # Has budget 'enrollment_20k_50k': +5, # Sweet spot size 'growth_corridor': +3, # Expanding district } def score_lead(lead, district): score = 50 # Base score for signal, points in SCORING_SIGNALS.items(): if has_signal(lead, district, signal): score += points return min(score, 100)
LeadDistrictSignalsScore
Sha RogersLeander ISDSafety Dir + Sweet Spot + Growth95
Dr. Bruce GearingLeander ISDHas Safety Dir + Sweet Spot92
Dr. Rick WestfallKeller ISDSweet Spot + Growth88
07
Storage

PostgreSQL Data Warehouse

Store all districts, leads, scores, and campaign data in a structured database for tracking and analytics.

schema.sql View Schema
CREATE TABLE leads ( id UUID PRIMARY KEY, district_id UUID REFERENCES districts(id), full_name VARCHAR(255), title VARCHAR(255), email VARCHAR(255) UNIQUE, persona VARCHAR(50), -- from Step 3 lead_score INTEGER, -- from Step 6 status VARCHAR(50) DEFAULT 'new', campaign_id VARCHAR(100), created_at TIMESTAMP DEFAULT NOW() );
PostgreSQL Supabase
Phase 3

Automated Outreach

Deploy personalized, multi-touch email campaigns at scale.

08
Outreach

Push to Instantly.ai

Add scored leads to campaigns via API. Separate tracks for Superintendents vs Safety Directors.

instantly_push.py View Code
# Route leads to persona-specific campaigns CAMPAIGNS = { 'superintendent': 'camp_tx_superintendents_q1_2026', 'safety_director': 'camp_tx_safety_q1_2026', } def push_lead(lead): campaign = CAMPAIGNS[lead['persona']] instantly.add_lead({ "campaign_id": campaign, "email": lead['email'], "first_name": lead['first_name'], "custom_variables": { "district": lead['district'], "score": lead['score'] } })
Instantly.ai REST API
09
Personalization

Spintax Email Variations

Generate 40+ unique email variations for deliverability and A/B testing at scale.

email_template.txt View Templates
# Subject Spintax {After Antioch|Following January|Since the incident} - {quick question|brief thought} # Body Spintax {Hi|Hey|Hello} {{first_name}}, {After Antioch|Following the incident}, {superintendents|district leadersrethinking|reconsidering} their security approach... # Result: 40+ unique combinations
Spintax A/B Testing
10
Campaign Setup

Email Sequences

3-touch cadence over 10 days. Initial outreach, follow-up, and breakup.

sequence_config.json
{ "sequence": [ {"step": 1, "delay": 0, "type": "initial"}, {"step": 2, "delay": 3, "type": "follow_up"}, {"step": 3, "delay": 5, "type": "breakup""send_window": "8am-5pm CST" }
Instantly.ai Sequences
Phase 4

Automation & Optimization

Real-time monitoring, instant alerts, and continuous improvement.

11
Automation

n8n Webhooks + Slack Alerts

Real-time notifications when leads reply. Auto-log to database and alert the team instantly.

n8n_workflow.json View Workflow
// Workflow: Instantly Reply Handler { "trigger": "Instantly Webhook", "on_event": "reply", "actions": [ "Parse reply + sentiment", "Log to PostgreSQL", "Slack → #gtm-hot-leads", "Update lead status" ] }
n8n Slack Webhooks
12
Analytics

Metabase Dashboard

Real-time campaign performance. Open rates, reply rates, meetings by tier and persona.

dashboard_queries.sql View Queries
-- Performance by Persona SELECT persona, COUNT(*) as sent, ROUND(100.0 * SUM(opened)/COUNT(*)) as open_rate, ROUND(100.0 * SUM(replied)/COUNT(*)) as reply_rate FROM leads GROUP BY persona;
Metabase Real-time
13
Optimization

Data-Driven Iteration

Analyze results, find winning patterns, double down on what works.

analysis_results.txt
# A/B Test Results (Week 1) Subject Lines: "After Antioch"45% open, 12% reply WINNER "Quick question"18% open, 3% reply Drop Personas: Safety Directors → 67% reply rate Prioritize Superintendents → 11% reply rate Needs champion # Action: Double down on urgency + Safety Directors
Analysis Iteration
14
Victory

WIN — Meeting Booked

Meeting booked. Dual-thread success with Leander ISD. System works.

// Pipeline Results { "districts_scraped": 254, "icp_filtered": 98, "leads_enriched": 147, "leads_scored": 147, "emails_sent": 147, "replies": 18, // 12% reply rate "meetings_booked": 4, "highlight": "Leander ISD - dual thread success", "status": "This is not a pitch. It's a running system." }
Production Ready Scalable Repeatable
🚀

Built to Run

I didn't want to describe a strategy. I wanted to build one. Every script runs. Every lead is real. The pipeline is live.