How to build an AI sales agent

The writing is the easy half of outbound. The hard half is sending it, across warmed and rotated mailboxes, and catching every reply. HotHawk is that half. This guide gives your AI a set of hands: three ways to wire a model to HotHawk, with code you can run today.

Start your 7 day free trial

No credit card required.

Reviewed July 2026

Every "AI sales agent" is two things wearing one coat: a brain that decides who to email and what to say, and a pair of hands that actually sends it and deals with what comes back. Most tools give you a clever brain bolted to one weak sending account. That is where campaigns quietly stall.

HotHawk is the hands. It sends across every mailbox you connect, rotating so no single inbox carries the load, with warmup running on real Google and Microsoft inboxes. There is headroom to push 40,000 a day and more. And the Master Inbox catches every reply, including the forwarded and out-of-office ones, so the agent never loses a warm lead in a thread it forgot about.

So the job on this page is narrow: bring the brain, and connect it to the hands. Three ways to do that, from an afternoon prototype to a scheduled production loop.

Path A

Claude Agent SDK

You host it. An agent loop calls HotHawk over REST or MCP.

Path B

Routines

Scheduled cloud agents. No server, no laptop kept open.

Path C

Cloudflare Workers

Cron plus a free-tier worker calling HotHawk over REST.

Path A

The Claude Agent SDK, hosted by you

The Claude Agent SDK is Claude Code packaged as a library. It brings the agent loop, tool use and file handling; you bring the tools it should reach for. To give it HotHawk, you wrap the REST API as a tool and let the model call it. Python and TypeScript both ship it: claude-agent-sdk on PyPI, @anthropic-ai/claude-agent-sdk on npm.

First, a thin HotHawk REST client

This is the piece the agent will call. It creates a campaign, activates it, and reads replies. REST uses a Bearer token, which is exactly what you want for anything running unattended.

python
import os, httpx

class HotHawk:
    """A thin client over the HotHawk REST API. Bearer auth, JSON in and out.
    Exact paths and payloads are in the API reference at /developers."""

    def __init__(self, token=None, base="https://api.hothawk.ai"):
        self.base = base
        self.h = {"Authorization": f"Bearer {token or os.environ['HOTHAWK_API_KEY']}"}

    def create_campaign(self, name, list_id):
        r = httpx.post(f"{self.base}/v1/campaigns", headers=self.h,
                       json={"name": name, "list_id": list_id})
        r.raise_for_status()
        return r.json()

    def activate(self, campaign_id):
        httpx.post(f"{self.base}/v1/campaigns/{campaign_id}/activate",
                   headers=self.h).raise_for_status()

    def replies(self, since=None):
        r = httpx.get(f"{self.base}/v1/threads", headers=self.h,
                      params={"status": "replied", "since": since})
        r.raise_for_status()
        return r.json()["data"]

Then, the agent loop

Register the client as an in-process tool, name the model, and hand the SDK a prompt. The loop runs itself: Claude decides, calls the tool, reads the result, and keeps going until the job is done.

python
# pip install claude-agent-sdk   (Python 3.10+; bundles the Claude Code binary)
import anyio
from claude_agent_sdk import query, ClaudeAgentOptions, tool, create_sdk_mcp_server

hh = HotHawk()  # the REST client above

@tool("launch_campaign", "Create and activate a cold email campaign in HotHawk",
      {"name": str, "list_id": str})
async def launch_campaign(args):
    campaign = hh.create_campaign(args["name"], args["list_id"])
    hh.activate(campaign["id"])
    return {"content": [{"type": "text", "text": f"Launched campaign {campaign['id']}"}]}

# Wrap the REST client as an in-process tool the agent can call.
hothawk = create_sdk_mcp_server(name="hothawk", version="1.0.0", tools=[launch_campaign])

options = ClaudeAgentOptions(
    model="claude-opus-4-8",
    mcp_servers={"hothawk": hothawk},
    allowed_tools=["mcp__hothawk__launch_campaign"],
    system_prompt="You run outbound for a B2B SaaS. Propose, then act. Keep the human in control.",
)

async def main():
    async for message in query(prompt="Launch a campaign to the Q3 SaaS list.", options=options):
        print(message)

anyio.run(main)

The TypeScript SDK mirrors it almost line for line:

typescript
// npm install @anthropic-ai/claude-agent-sdk   (Node 18+; same shape as Python)
import { query, tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk";

const launchCampaign = tool(
  "launch_campaign",
  "Create and activate a cold email campaign in HotHawk",
  { name: String, list_id: String },
  async ({ name, list_id }) => {
    const c = await hh.createCampaign(name, list_id);
    await hh.activate(c.id);
    return { content: [{ type: "text", text: `Launched campaign ${c.id}` }] };
  },
);

const hothawk = createSdkMcpServer({ name: "hothawk", version: "1.0.0", tools: [launchCampaign] });

for await (const message of query({
  prompt: "Launch a campaign to the Q3 SaaS list.",
  options: { model: "claude-opus-4-8", mcpServers: { hothawk } },
})) {
  console.log(message);
}

You could instead point the SDK straight at the hosted HotHawk MCP server. That works well when a person is in the loop signing in with OAuth. For a headless service, the REST tool above is the cleaner fit.

Path B

Routines, for scheduled cloud runs

If you want an agent that runs on its own without you standing up any infrastructure, routines are the shortest road. A routine is a saved Claude Code setup: a prompt, any repositories or connectors it needs, packaged once and run on Anthropic's cloud. It keeps working with your laptop shut.

Create one at claude.ai/code/routines, or from Claude Code with the schedule command:

claude code
# From Claude Code, or at claude.ai/code/routines in the browser
/schedule

Each routine takes one or more triggers: a schedule (hourly, nightly, weekly), an API endpoint you POST to with a bearer token, or a GitHub event. For outbound, a nightly routine that reads yesterday's replies through the HotHawk connector, sorts the warm ones and drafts responses is a natural fit.

One naming trap. "Claude Code desktop routines" is a misnomer you will see around. Routines are not a desktop feature that needs your machine awake. They are web and cloud hosted, which is the whole point: they run when you are asleep.

Path C

Cloudflare Workers, on the free tier

If you would rather own the whole loop and pay nothing to start, Cloudflare Workers gives you a cron trigger and a worker in the same place. As of July 2026 the Workers free tier includes cron triggers and SQLite-backed Durable Objects, so a scheduled agent that keeps state between runs costs nothing to stand up. The brain lives on Cloudflare; HotHawk does the sending over REST.

The cron trigger

A few lines of wrangler.toml put the worker on a schedule:

wrangler.toml
name = "hothawk-sdr"
main = "src/index.ts"
compatibility_date = "2026-07-01"

# Cron triggers run on the Workers free tier at no extra cost (July 2026).
[triggers]
crons = ["0 8 * * 1-5"]   # 08:00 UTC, Monday to Friday

[vars]
HOTHAWK_BASE = "https://api.hothawk.ai"

# Secrets are set out of band, never in the file:
#   wrangler secret put HOTHAWK_API_KEY
#   wrangler secret put ANTHROPIC_API_KEY

The scheduled handler

The cron fires the scheduled handler. It reads replies, then launches and activates today's campaign, all through HotHawk's REST API:

src/index.ts
export interface Env {
  HOTHAWK_BASE: string;
  HOTHAWK_API_KEY: string;
  ANTHROPIC_API_KEY: string;
}

export default {
  // The cron trigger calls this handler. Nothing to keep running between shifts.
  async scheduled(event: ScheduledController, env: Env, ctx: ExecutionContext) {
    ctx.waitUntil(runShift(env));
  },
};

async function runShift(env: Env) {
  const h = {
    Authorization: `Bearer ${env.HOTHAWK_API_KEY}`,
    "content-type": "application/json",
  };

  // 1. Read overnight replies from HotHawk.
  const { data: replies } = await fetch(
    `${env.HOTHAWK_BASE}/v1/threads?status=replied`, { headers: h },
  ).then((r) => r.json());

  // 2. (optional) Let Claude decide what to do with each one. See the brain below.

  // 3. Launch today's campaign through HotHawk. HotHawk does the sending.
  const campaign = await fetch(`${env.HOTHAWK_BASE}/v1/campaigns`, {
    method: "POST", headers: h,
    body: JSON.stringify({ name: "Daily SaaS outbound", list_id: "lst_q3_saas" }),
  }).then((r) => r.json());

  await fetch(`${env.HOTHAWK_BASE}/v1/campaigns/${campaign.id}/activate`, {
    method: "POST", headers: h,
  });
}

The brain, on Cloudflare

Drop a single Claude call into the same worker to decide what each reply deserves. Route it through an AI Gateway endpoint if you want caching and logs, or call the Messages API directly:

src/index.ts
// The brain: one Claude call from inside the Worker.
// Swap the URL for your AI Gateway endpoint to add caching, retries and logs.
async function draftReply(env: Env, incoming: string) {
  const res = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": env.ANTHROPIC_API_KEY,
      "anthropic-version": "2023-06-01",
      "content-type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-opus-4-8",
      max_tokens: 1024,
      messages: [{ role: "user", content: `Draft a short reply to this prospect:\n\n${incoming}` }],
    }),
  }).then((r) => r.json());
  return res.content[0].text;
}

For a stateful agent that remembers what it did last run, the Cloudflare Agents SDK (the agents package) gives each agent its own Durable Object with a SQLite database. You can keep the brain on Workers AI too, though a call to Claude usually gets you further on reply drafting.

So which one?

To prove the idea in an afternoon, skip the code entirely: connect the HotHawk MCP server to Claude and run it from a chat. You will know within an hour whether the workflow holds up. (the MCP setup guide walks it through.)

To productionise it, reach for the Agent SDK (Path A) if you have somewhere to host it, or Cloudflare Workers (Path C) if you want cron and compute for nothing. Routines (Path B) sit in between when you want a scheduled cloud run and no infrastructure at all.

One rule holds across all three: for an unattended loop, authenticate with the REST API and a Bearer token, not the OAuth MCP server. The MCP server is made for an interactive Claude session with a person signing in. A cron job has no one to click Allow.

AI sales agent FAQs

Is this the same as an AI SDR?

Not quite, and that is the point. An AI SDR is sold as a hands-off black box that runs outbound for you. Here, the AI is the brain you choose and control, and HotHawk is the hands: it does the sending, the mailbox rotation, the warmup and the reply capture underneath. You get the automation without handing over the keys.

Should the agent talk to HotHawk over MCP or REST?

Depends on whether a human is present. For an interactive Claude session, use the MCP server: you sign in once with OAuth and run it from a chat. For an unattended loop that runs on a schedule with nobody watching, use the REST API with a Bearer token. The MCP server is built around an interactive OAuth flow, so it is the wrong tool for a 3am cron job.

Which Claude model should the brain use?

claude-opus-4-8 is the sensible default for agent work: it plans well and stays coherent over long runs. Drop to claude-sonnet-5 when you are running high volume and want to spend less, claude-haiku-4-5 for fast, simple jobs like sorting replies, and claude-fable-5 for the hardest reasoning. You can mix them: a cheap model to triage, a stronger one to write.

Does letting an agent run outbound hurt deliverability?

No. Whoever hits launch, whether it is you or an agent, the sending still rotates across your warmed mailboxes and warmup keeps running on real inboxes. The agent runs the campaign, but it cannot switch off the protections underneath it. We describe those practices rather than promising any particular result, because deliverability depends on your list and copy too.

Do I need to be a developer to build one?

For the fastest version, no. Connect the HotHawk MCP server to Claude and run it from a chat, or set up a routine at claude.ai/code/routines with a plain-language prompt. The Agent SDK and Cloudflare paths do need some code, but they are the productionising step, not the starting point.

Send cold emails that get delivered. Never miss a positive reply.

Serious deliverability paired with the best reply management in the market.

Start your 7 day free trial

No credit card required.

Premium warmup

Join our premium warmup pool

We have over 50,000 Google and Microsoft mailboxes in the pool and we are opening to the public soon. Be first to know when it's open.

Special offer

Get 50% more sending, FREE.

Send 50% extra emails per month on any plan, every month for as long as you're with us. Enter your details and we'll email your promo code over.

Your new boosted limits

  • Starter 100,000 150,000
  • Scale 300,000 450,000
  • Infra 500,000 750,000

Applies to any plan. One per customer.