DEV Community

Robert Pelloni
Robert Pelloni

Posted on • Originally published at tormentnexus.site

Automating Technical Outreach: The 7-State Pipeline for AI-Driven Early Adopter Acquisition

Automating Technical Outreach: The 7-State Pipeline for AI-Driven Early Adopter Acquisition

Stop cold outreach. This technical deep-dive reveals a 7-state AI pipeline—Discovered → Researched → Outreach → Engaged → Negotiating → Won/Lost—that systematically finds and converts early adopters for developer tools. Packed with real code, real numbers, and a repeatable framework.

Why Technical Outreach Fails Without a State Machine

Most developer marketing teams treat outreach as a single action: send email, wait, follow up. This naive approach ignores the reality of early adopter acquisition. A lead isn't a binary "yes/no"—it's a state machine. At TormentNexus, our first beta launch in 2023 saw a 4.2% conversion rate from cold contact to sign-up. After implementing a 7-state pipeline with automated transitions, that rate jumped to 18.7% over 90 days. The secret? Explicit state transitions driven by behavioral triggers, not calendar dates.

The pipeline we built tracks each prospect through exactly seven states: Discovered, Researched, Outreach, Engaged, Negotiating, Won, or Lost. Each transition is deterministic—guided by a JSON state machine and a lightweight agent that evaluates custom conditions. No more "let's try again in 2 weeks." Every move has a precise trigger: a reply, a repository star, a job posting update, or a silence threshold.

The core insight: early adopters are not "leads." They are humans evaluating a technical solution. Your AI outreach must respect their context, not your pipeline deadlines. The 7-state model forces you to gather real signals before you reach out—and to pivot when you get a response.

Discovered: Signal-Based Prospecting with Automated Lead Generation AI

The first state isn't about volume; it's about signal density. Our team deployed a custom scraper that monitors three feeds: GitHub repositories matching specific tech stacks, Hacker News "Show HN" threads with 3+ upvotes in the first hour, and changelogs from competing tools. The scraper is a Python script using httpx and BeautifulSoup that runs every 6 hours on a cron job. It outputs a JSONL file with initial data—username, profile URL, and a timestamp.

# discovery_agent.py — Simplified signal capture
import httpx
from bs4 import BeautifulSoup
import json
import time

def check_github_repos(topic="llm-agent", min_stars=50):
    url = f"https://api.github.com/search/repositories?q={topic}+stars:>{min_stars}&sort=updated"
    response = httpx.get(url, headers={"Accept": "application/vnd.github.v3+json"})
    return response.json()["items"]

def check_hackernews_show():
    response = httpx.get("https://hacker-news.firebaseio.com/v0/showstories.json")
    story_ids = response.json()[:20]
    for story_id in story_ids:
        item = httpx.get(f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json").json()
        if item.get("score", 0) >= 3:
            yield {"source": "hn", "username": item["by"], "score": item["score"],
                   "title": item.get("title", ""), "timestamp": time.time()}

# Write to discovery queue
with open("discovery_queue.jsonl", "a") as f:
    for repo in check_github_repos("developer-tools", 100):
        f.write(json.dumps({"state": "discovered", "data": repo, "timestamp": time.time()}) + "\n")
    for story in check_hackernews_show():
        f.write(json.dumps({"state": "discovered", "data": story, "timestamp": time.time()}) + "\n")

Over 30 days, this discovered 847 unique early adopter candidates. But 35% were noise—job hoppers, fork-only repos, or spam. The pipeline's next state filters aggressively. The key metric here isn't total discovered—it's signal-to-noise ratio. We aim for 1:3 (one quality lead per three entries).

Researched: Enrichment and Scoring Before First Contact

Once a candidate enters Discovered, an agent triggers a research pipeline. This state is mandatory—no outbound email happens without a fully enriched profile. The agent uses the langchain framework to call three APIs in parallel: the GitHub API for commit history and repos, the Stack Overflow API for answer history, and a custom employment-based scraper for LinkedIn profile snippets (legal via public profile scraping). Each API returns structured data that fills a template:

# research_agent.py — Enrichment step
from langchain.tools import tool
from langchain.agents import initialize_agent, AgentType
import json

@tool
def enrich_github(handle: str) -> dict:
    """Fetches public GitHub data."""
    response = requests.get(f"https://api.github.com/users/{handle}/repos")
    repos = response.json()
    total_stars = sum(r["stargazers_count"] for r in repos)
    languages = list(set(r["language"] for r in repos if r["language"]))
    return {"total_stars": total_stars, "languages": languages, "repo_count": len(repos)}

@tool
def score_lead(research: dict) -> float:
    """Score from 0.0 to 1.0 based on fit with developer product."""
    score = 0.0
    if "Python" in research.get("languages", []):
        score += 0.3
    if research.get("total_stars", 0) > 200:
        score += 0.4
    if research.get("repo_count", 0) > 5:
        score += 0.2
    return min(score, 1.0)

# Example: for a discovered handle "msmith_dev"
profile = enrich_github("msmith_dev")
profile["score"] = score_lead(profile)
# Save to enriched database
with open("enriched_profiles.jsonl", "a") as f:
    f.write(json.dumps({"state": "researched", "profile": profile}) + "\n")

Only candidates with a score > 0.6 transition to Outreach. This threshold filtered out 340 of 847 candidates in our trial (40%). The remaining 507 prospects had an average of 23 commits, 4.2 repos, and 350 GitHub stars each. The research phase consumes 2 seconds per candidate via API calls—total cost per 1000: $2.10 in API credits. This is your lead generation AI working silently in the background.

Outreach: Hyper-Personalized First Messages via Automated Sales Templates

Now the agent writes the email. Not a template with a merge field. A real, context-aware draft using the enriched profile. The agent receives a persona ("Developer Advocate at TormentNexus") and the research data—then generates a first-draft email with three sections: a specific compliment (e.g., "I noticed your work on the K8s operator for Redis"), a problem statement (e.g., "Many devs spend 5+ hours debugging SDK bugs per sprint"), and a 1-sentence solution teaser. The llm_call uses GPT-4 with a specific system prompt to avoid markdown and keep tone technical but warm:

# outreach_agent.py — generates and sends automated outreach
import openai
from smtplib import SMTP

system_prompt = """
You are a developer advocate for a new developer tool. Write a brief, personal email (max 3 paragraphs) 
to a developer discovered through open source contributions. Reference their actual repos or projects. 
Focus on a specific pain point common in their stack. End with a low-friction ask: a 15-min screening call. 
Keep language direct, no fluff, no emoji. Use the data provided.
"""

def generate_outreach(profile: dict) -> str:
    context = f"User {profile['handle']} has repos in {profile['languages']}, {profile['total_stars']} stars total."
    response = openai.ChatCompletion.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": f"Write outreach for: {context}"}
        ]
    )
    return response.choices[0].message.content

# Store state transition
def transition_to_outreach(profile):
    email_body = generate_outreach(profile)
    # Send via SMTP (details omitted)
    return {"state": "outreach", "email_sent": True, "timestamp": time.time()}

Our A/B test: personalized emails (with custom intros) had a 41% open rate and 12.5% reply rate vs. generic templates at 23% open and 4.1% reply. The AI outreach phase runs in batches of 50 per day—respecting time zones via a simple UTC offset lookup.

Engaged → Negotiating: Behavioral Transitions and Demo Automation

Engagement is defined as a reply (any substance) or a direct calendar booking. Our mail server tracks via a webhook: when a prospect replies, the pipeline transitions to Engaged. From there, a separate agent schedules a live demo. We use a 15-minute Zoom slot via the Calendly API—but only if the prospect's timezone is 10am-4pm local. The Engaged state has a 72-hour inactivity timer; no response in 72 hours means automatic transition to Lost (with one final nudge email).

Negotiating means the prospect agreed to a post-demo onboarding call. This state uses a different agent that sends a technical onboarding checklist via email (API docs, a sandbox environment link, and a 1-hour workshop invite). No pricing discussion yet—that happens only after the prospect tests the sandbox. In our data, 38% of prospects who entered Negotiating moved to Won (paid enterprise trial), while 62% dropped to Lost after 2 weeks of no sandbox activity.

The transition logic is deterministic JSON:

# state_transitions.py — controller logic
TRANSITIONS = {
    "discovered": {"to": "researched", "trigger": "enrichment_complete"},
    "researched": {"to": "outreach", "trigger": "score > 0.6"},
    "outreach": {"to": "engaged", "trigger": "replied_or_booked"},
    "outreach": {"to": "lost", "trigger": "no_response_7_days"},
    "engaged": {"to": "negotiating", "trigger": "demo_completed"},
    "engaged": {"to": "lost", "trigger": "no_activity_72_hours"},
    "negotiating": {"to": "won", "trigger": "sandbox_active_for_7_days"},
    "negotiating": {"to": "lost", "trigger": "sandbox_inactive_2_weeks"}
}

This clarity means any engineer can modify pipeline behavior without touching the underlying agent code. The state machine runs as a daemon process, polling a Redis-Redis-backed queue every 5 minutes for events.

Won vs. Lost: Feedback Loops That Close the Cycle

The final states aren't endpoints—they're feedback generators. When a prospect reaches Won (active paying user for 30+ days), the pipeline logs their entire journey into a training dataset for the outreach agent. The LLM model is fine-tuned on which email structures and pain-point references converted best. For Lost prospects, the pipeline sends a "thank you for your time" email with a 2-question survey link (multiple choice: "lack of feature" or "timing" or "pricing"). The survey responses update a weighting system in the scoring agent's score_lead() function—so future prospects who match Lost patterns get lower scores and are deprioritized.

A concrete example: Early in our pipeline, we noticed 42% of Lost prospects cited "pricing" as the main blocker. We adjusted the outreach agent to never mention pricing in the first email—only after the Negotiating state. That single change increased the Engaged to Negotiating conversion by 19%.

The developer marketing team reviews the pipeline's weekly summary


Originally published at tormentnexus.site

Top comments (0)