DEV Community

Cover image for Build an AI SDR That Books Meetings While You Sleep (Without the Spam)
Michael
Michael

Posted on Originally published at getmichaelai.com

Build an AI SDR That Books Meetings While You Sleep (Without the Spam)

Most "AI SDR" tools are just spray-and-pray email blasters with a language model bolted on. They burn your domain reputation and annoy prospects.

A real AI SDR agent is different. It's a system that researches a lead, decides whether they're worth contacting, writes something a human would actually reply to, handles the back-and-forth, and drops a booked meeting on your calendar. All while you're asleep.

Here's how to build one that works.

The architecture

An AI SDR isn't one prompt. It's a pipeline of specialized steps, each with a single job:

  1. Enrichment - turn a name and company into context.
  2. Qualification - score the lead and kill the bad fits.
  3. Message generation - write a personalized first touch.
  4. Send + track - deliver and watch for replies.
  5. Reply handling - classify intent and respond.
  6. Booking - hand off to a calendar link when there's interest.

Treat each as a discrete function. This makes the agent debuggable and stops the LLM from hallucinating its way through five jobs at once.

Step 1: Enrich before you write anything

Bad personalization is worse than none. "I noticed you work at [Company]" fools no one. You need real signals: recent funding, a job posting, a tech stack change, a LinkedIn post.

def enrich_lead(lead):
    company = lead["company_domain"]
    signals = {
        "funding": get_funding_news(company),
        "hiring": get_open_roles(company),
        "tech_stack": detect_tech(company),
        "recent_posts": get_linkedin_activity(lead["linkedin"]),
    }
    # Drop empty signals so the model doesn't invent them
    return {k: v for k, v in signals.items() if v}
Enter fullscreen mode Exit fullscreen mode

Feed only real signals into the prompt. If enrichment returns nothing meaningful, the lead shouldn't get a personalized line at all - fall back to a segment-level angle instead of faking specificity.

Step 2: Qualify ruthlessly

Your AI SDR's value isn't sending more email. It's sending fewer, better emails to people who might buy. Use the LLM as a judgment layer.

def qualify(lead, signals, icp):
    prompt = f"""
    Ideal customer profile: {icp}
    Lead: {lead['title']} at {lead['company']}
    Signals: {signals}

    Score 0-100 for fit. Return JSON:
    {{"score": int, "reason": str, "angle": str}}
    Be strict. Score below 40 if the title or company size is off.
    """
    result = call_llm(prompt, json_mode=True)
    return result
Enter fullscreen mode Exit fullscreen mode

Anything under your threshold gets discarded or routed to a nurture list. The angle field becomes the hook for the message - the single most relevant reason to reach out.

Step 3: Write like a human, not a template

The first email has one job: earn a reply. Keep it short, reference one real signal, and make the ask tiny.

def write_email(lead, angle):
    prompt = f"""
    Write a cold email to {lead['first_name']}, {lead['title']}.
    Hook: {angle}

    Rules:
    - Under 90 words
    - No corporate jargon, no "I hope this finds you well"
    - One specific observation, one clear value point
    - End with a soft question, not a hard pitch
    - Sound like a founder, not a marketing team
    """
    return call_llm(prompt)
Enter fullscreen mode Exit fullscreen mode

Run the output through a quick quality gate: reject anything with "synergy," "revolutionize," or more than one exclamation mark. Small guardrails keep the model honest.

Step 4: Handle the reply - this is where the money is

Anyone can automate sending. The agent earns its keep by managing responses without a human in the loop. Classify every reply first:

def classify_reply(text):
    prompt = f"""
    Classify this email reply into one:
    INTERESTED, NOT_NOW, OBJECTION, WRONG_PERSON, UNSUBSCRIBE
    Reply: "{text}"
    Return only the label.
    """
    return call_llm(prompt).strip()

HANDLERS = {
    "INTERESTED": send_booking_link,
    "OBJECTION": handle_objection,
    "NOT_NOW": schedule_followup,
    "WRONG_PERSON": ask_for_referral,
    "UNSUBSCRIBE": mark_suppressed,
}

def process(reply):
    HANDLERS[classify_reply(reply.text)](reply)
Enter fullscreen mode Exit fullscreen mode

When intent is positive, the agent offers times immediately - either by proposing slots from a calendar API or dropping a scheduling link. No lag, no "a rep will get back to you." The gap between interest and booking is where deals die.

Step 5: Close the loop with booking

Wire the INTERESTED path to your calendar. Use a Cal.com or Google Calendar API to fetch open slots and confirm the meeting programmatically, then write it to your CRM so a human takes over prepared, not cold.

def send_booking_link(reply):
    slots = get_open_slots(days=5)
    msg = draft_scheduling_email(reply.lead, slots)
    send(reply.lead, msg)
    crm.update(reply.lead, stage="meeting_pending")
Enter fullscreen mode Exit fullscreen mode

Guardrails that keep you out of the spam folder

  • Rate limits. Warm up domains. Cap sends per mailbox per day (start at 30-40).
  • Suppression list. Never re-contact opt-outs or existing customers.
  • Human review queue. Route ambiguous or high-value replies to a person.
  • Kill switch. If bounce rate spikes, pause automatically.

The realistic picture

An AI SDR won't replace your best closer. It replaces the tedious top of the funnel - research, first touch, follow-up, and initial qualification - so humans spend their time on live conversations instead of copy-pasting.

Built right, it runs overnight and hands you a calendar full of qualified meetings by morning. Built lazily, it's a reputation-destroying spam cannon.

The difference is entirely in the qualification and reply-handling layers. Send less. Respond fast. Let judgment, not volume, do the work.


Originally published at getmichaelai.com

Top comments (0)