Tutorial // Sales2026-07-1517 min read

Building an Autonomous Lead Generation Agent using Eve.dev

Discover how to build an AI sales assistant that scores leads, crafts personalized emails, and uses durable wait states to manage multi-day follow-up sequences using Eve.dev.

Varun Raj Manoharan
Varun Raj Manoharan
Eve.devAI Lead GenerationCold EmailSales AgentTutorial

Summary

TL;DR: Drip campaigns are static and boring. Learn how to build an autonomous AI sales agent with Eve.dev that researches leads, writes hyper-personalized emails, and durably waits days before following up.

Traditional sales automation tools rely on static email templates and rigid flowcharts. If a prospect replies, the sequence breaks, and a human has to take over.

What if you could build an agent that behaves like an actual Sales Development Representative (SDR)? An agent that looks at a lead's LinkedIn, writes a custom email, waits 3 days, checks if they replied, and writes a contextual follow-up if they didn't?

Eve.dev makes this possible. Because Eve.dev workflows are durable, an agent can naturally "sleep" for days without consuming server resources, waking up exactly when needed with its memory intact.

The Agent Architecture

We will create an Eve.dev agent called sdr.

TEXT
/eve
  /agents
    /sdr
      instructions.md
      /tools
        research_company.ts
        send_email.ts
        wait_days.ts

Step 1: The SDR Instructions

In /eve/agents/sdr/instructions.md, we define the strategy:

MARKDOWN
# Autonomous SDR

You are a top-performing Sales Development Representative. Your goal is to get prospects to book a meeting.

## Workflow
1. Use `research_company` to find recent news about the prospect's company.
2. Write a highly personalized cold email based on that news and use `send_email`.
3. Use `wait_days` to pause execution for 3 days.
4. After waking up, evaluate if they replied (this context will be provided to you).
5. If no reply, write a shorter, polite follow-up. Do not be pushy.

Step 2: The Multi-Day Wait Tool

This is where Eve.dev shines. We will create a tool that suspends the agent and instructs the underlying system to wake it up later.

Create /eve/agents/sdr/tools/wait_days.ts:

TypeScript
import { tool, suspend } from '@eve/core';
import { z } from 'zod';

export default tool({
  name: 'wait_days',
  description: 'Pause the campaign and wait for a specified number of days before following up.',
  schema: z.object({
    days: z.number().min(1).max(14)
  }),
  execute: async ({ days }) => {
    // We calculate the timestamp for when the agent should wake up
    const wakeUpAt = new Date();
    wakeUpAt.setDate(wakeUpAt.getDate() + days);

    // suspend() saves the agent state and stops execution
    return suspend({
      type: 'scheduled_sleep',
      wakeUpAt: wakeUpAt.toISOString(),
      message: `Sleeping for ${days} days.`
    });
  }
});

Step 3: Sending the Email

Create /eve/agents/sdr/tools/send_email.ts:

TypeScript
import { tool } from '@eve/core';
import { z } from 'zod';
// e.g. import { Resend } from 'resend';

export default tool({
  name: 'send_email',
  description: 'Send an email to the prospect.',
  schema: z.object({
    to: z.string().email(),
    subject: z.string(),
    body: z.string()
  }),
  execute: async ({ to, subject, body }) => {
    console.log(`Sending email to ${to}...`);
    // await resend.emails.send({ ... });
    return { success: true, timestamp: new Date().toISOString() };
  }
});

Step 4: The Orchestrator (Cron Job)

Since the agent is suspending itself, how does it wake up? You need a simple background worker or a Cron job that checks the database for sleeping agents whose wakeUpAt time has passed.

TypeScript
import { Eve } from '@eve/core';

const eve = new Eve({ workspace: './eve' });

// This function runs every hour via a Cron job
async function wakeUpSleepingAgents() {
  // 1. Query your database for workflows where wakeUpAt <= NOW()
  const sleepingWorkflows = await getReadyWorkflowsFromDB();

  for (const workflow of sleepingWorkflows) {
    const agent = await eve.getAgent('sdr');
    
    // Check if the user replied via your email provider API
    const hasReplied = await checkInboxForReply(workflow.prospectEmail);
    
    // Resume the agent with the new context!
    await agent.resume({
      id: workflow.id,
      injectedMessage: hasReplied 
        ? "The prospect replied! STOP the automated sequence immediately."
        : "The prospect has not replied yet. It is time to send the follow-up."
    });
  }
}

Conclusion

With Eve.dev, you aren't just building chatbots; you are building autonomous employees. By using the suspend primitive, your agent can manage complex drip campaigns over weeks. And because it's powered by an LLM, the copy is dynamic, contextual, and deeply personalized—something traditional marketing software simply cannot do.

FAQ

How does Eve.dev remember what it emailed 3 days ago? When the suspend function is called, Eve.dev serializes the entire conversation history (including the exact email drafted by the LLM) into your database. When the agent resumes, this history is reloaded into the LLM's context window.

What happens if the prospect replies early? Your email webhook (e.g., from SendGrid or Postmark) can trigger an endpoint that finds the sleeping workflow by ID and calls agent.resume() early, injecting a message like "Prospect replied, stop campaign." This prevents the agent from sending an awkward follow-up.

Available for new projects

Let's build something great.

Have a project in mind? We are an elite software and AI development studio ready to bring your ideas to production. Let's talk about your roadmap.