Skip to main content
Campaigns let you run batch AI-driven outreach across WhatsApp, Telegram, and email. Instead of cold calling, Naturalead starts personalized conversations with each lead using your configured AI agent, qualifies them based on your criteria, and reports results in real time. This guide walks you through creating a campaign from scratch, launching it, and monitoring its progress.

Prerequisites

Before launching a campaign you need three things in place:

AI Agent

A configured agent with a system prompt, qualification stages, and optionally a knowledge base. Create one in the Bot page or via the Agent Config API.

Messaging Integration

At least one verified channel integration (Twilio for WhatsApp/SMS, Telegram bot, or Resend for email). Configure integrations in Settings > Integrations.

Leads

Leads in your account to reach out to. Import them via CSV, the Leads API, or a CRM sync webhook.
Campaigns send real messages to real people. Always test with a small lead set first by using manualLeadIds to target specific test leads before launching a broad filter-based campaign.

Step-by-step walkthrough

1
Choose and configure your AI agent
2
Your campaign needs an agent configuration that defines how the AI converses with leads. The agent includes a system prompt, conversation stages, qualification criteria, and guardrails.
3
If you already have an agent configured, note its agentConfigId. Otherwise, create one first:
4
curl
curl -X PUT "${API_URL}/api/agent-config" \
  -H "X-API-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sales Qualifier",
    "systemPrompt": "You are a friendly sales assistant qualifying inbound leads...",
    "goal": "Determine if the lead is a good fit for our product",
    "stages": [
      { "name": "introduction", "description": "Greet and introduce yourself" },
      { "name": "discovery", "description": "Ask about their needs and budget" },
      { "name": "qualification", "description": "Evaluate fit and next steps" }
    ]
  }'
Python
import requests

response = requests.put(
    f"{API_URL}/api/agent-config",
    headers={
        "X-API-Key": API_KEY,
        "Content-Type": "application/json",
    },
    json={
        "name": "Sales Qualifier",
        "systemPrompt": "You are a friendly sales assistant qualifying inbound leads...",
        "goal": "Determine if the lead is a good fit for our product",
        "stages": [
            {"name": "introduction", "description": "Greet and introduce yourself"},
            {"name": "discovery", "description": "Ask about their needs and budget"},
            {"name": "qualification", "description": "Evaluate fit and next steps"},
        ],
    },
)

agent_config = response.json()
agent_config_id = agent_config["_id"]
Node.js
const response = await fetch(`${API_URL}/api/agent-config`, {
  method: "PUT",
  headers: {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Sales Qualifier",
    systemPrompt:
      "You are a friendly sales assistant qualifying inbound leads...",
    goal: "Determine if the lead is a good fit for our product",
    stages: [
      { name: "introduction", description: "Greet and introduce yourself" },
      { name: "discovery", description: "Ask about their needs and budget" },
      { name: "qualification", description: "Evaluate fit and next steps" },
    ],
  }),
});

const agentConfig = await response.json();
const agentConfigId = agentConfig._id;
5
Create the campaign
6
Create a campaign by specifying your agent, target channel, lead filters, and schedule. The campaign starts in draft status so you can review it before launching.
7
curl
curl -X POST "${API_URL}/api/campaigns" \
  -H "X-API-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Q1 Outreach - WhatsApp",
    "agentConfigId": "'"${AGENT_CONFIG_ID}"'",
    "channel": "whatsapp",
    "leadFilter": {
      "statuses": ["new", "contacted"],
      "tags": ["inbound-2026"]
    },
    "schedule": {
      "startAt": "2026-04-01T09:00:00Z",
      "rateLimit": 10,
      "activeHours": {
        "start": "09:00",
        "end": "17:00",
        "timezone": "America/New_York"
      },
      "activeDays": [1, 2, 3, 4, 5]
    }
  }'
Python
response = requests.post(
    f"{API_URL}/api/campaigns",
    headers={
        "X-API-Key": API_KEY,
        "Content-Type": "application/json",
    },
    json={
        "name": "Q1 Outreach - WhatsApp",
        "agentConfigId": agent_config_id,
        "channel": "whatsapp",
        "leadFilter": {
            "statuses": ["new", "contacted"],
            "tags": ["inbound-2026"],
        },
        "schedule": {
            "startAt": "2026-04-01T09:00:00Z",
            "rateLimit": 10,
            "activeHours": {
                "start": "09:00",
                "end": "17:00",
                "timezone": "America/New_York",
            },
            "activeDays": [1, 2, 3, 4, 5],
        },
    },
)

campaign = response.json()
campaign_id = campaign["_id"]
Node.js
const campaignResponse = await fetch(`${API_URL}/api/campaigns`, {
  method: "POST",
  headers: {
    "X-API-Key": API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Q1 Outreach - WhatsApp",
    agentConfigId: agentConfigId,
    channel: "whatsapp",
    leadFilter: {
      statuses: ["new", "contacted"],
      tags: ["inbound-2026"],
    },
    schedule: {
      startAt: "2026-04-01T09:00:00Z",
      rateLimit: 10,
      activeHours: {
        start: "09:00",
        end: "17:00",
        timezone: "America/New_York",
      },
      activeDays: [1, 2, 3, 4, 5],
    },
  }),
});

const campaign = await campaignResponse.json();
const campaignId = campaign._id;
8
The leadFilter matches leads dynamically at launch time. You can also use manualLeadIds to target specific leads, or combine both approaches. Use advancedFilter with custom conditions for more granular targeting.
9
Preview matched leads
10
Before launching, review the campaign to verify it is configured correctly and check the lead count. The campaign object includes a stats.totalLeads field after creation that reflects how many leads match your filter.
11
curl
curl "${API_URL}/api/campaigns/${CAMPAIGN_ID}" \
  -H "X-API-Key: ${API_KEY}"
Python
response = requests.get(
    f"{API_URL}/api/campaigns/{campaign_id}",
    headers={"X-API-Key": API_KEY},
)

campaign = response.json()
print(f"Matched leads: {campaign['stats']['totalLeads']}")
print(f"Channel: {campaign['channel']}")
print(f"Status: {campaign['status']}")
Node.js
const detailResponse = await fetch(
  `${API_URL}/api/campaigns/${campaignId}`,
  {
    headers: { "X-API-Key": API_KEY },
  }
);

const detail = await detailResponse.json();
console.log(`Matched leads: ${detail.stats.totalLeads}`);
console.log(`Channel: ${detail.channel}`);
console.log(`Status: ${detail.status}`);
12
If you need to adjust the campaign, update it while it is still in draft status:
13
curl -X PATCH "${API_URL}/api/campaigns/${CAMPAIGN_ID}" \
  -H "X-API-Key: ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{ "schedule": { "rateLimit": 5 } }'
14
Launch the campaign
15
Once you are satisfied with the configuration, launch the campaign. This transitions it from draft to running and begins sending messages according to the schedule.
16
curl
curl -X POST "${API_URL}/api/campaigns/${CAMPAIGN_ID}/launch" \
  -H "X-API-Key: ${API_KEY}"
Python
response = requests.post(
    f"{API_URL}/api/campaigns/{campaign_id}/launch",
    headers={"X-API-Key": API_KEY},
)

campaign = response.json()
print(f"Status: {campaign['status']}")  # "running"
Node.js
const launchResponse = await fetch(
  `${API_URL}/api/campaigns/${campaignId}/launch`,
  {
    method: "POST",
    headers: { "X-API-Key": API_KEY },
  }
);

const launched = await launchResponse.json();
console.log(`Status: ${launched.status}`); // "running"
17
Launching a campaign is not reversible in terms of messages already sent. You can pause the campaign to stop further outreach, but messages that have already been delivered cannot be recalled.
18
Monitor progress and stats
19
Poll the campaign endpoint to track progress. The stats object updates in real time as the AI agent converses with leads.
20
curl
curl "${API_URL}/api/campaigns/${CAMPAIGN_ID}" \
  -H "X-API-Key: ${API_KEY}" \
  | jq '.stats'
Python
import time

while True:
    response = requests.get(
        f"{API_URL}/api/campaigns/{campaign_id}",
        headers={"X-API-Key": API_KEY},
    )
    stats = response.json()["stats"]
    status = response.json()["status"]

    print(f"Sent: {stats['sent']}/{stats['totalLeads']}")
    print(f"Replied: {stats['replied']}")
    print(f"Qualified: {stats['qualified']}")
    print(f"Failed: {stats['failed']}")

    if status == "completed":
        print("Campaign completed!")
        break

    time.sleep(30)
Node.js
const pollCampaign = async () => {
  const response = await fetch(
    `${API_URL}/api/campaigns/${campaignId}`,
    {
      headers: { "X-API-Key": API_KEY },
    }
  );

  const data = await response.json();
  const { stats, status } = data;

  console.log(`Sent: ${stats.sent}/${stats.totalLeads}`);
  console.log(`Replied: ${stats.replied}`);
  console.log(`Qualified: ${stats.qualified}`);
  console.log(`Failed: ${stats.failed}`);

  return status;
};

// Poll every 30 seconds
const interval = setInterval(async () => {
  const status = await pollCampaign();
  if (status === "completed") {
    console.log("Campaign completed!");
    clearInterval(interval);
  }
}, 30000);
21
To pause a running campaign and stop further outreach:
22
curl -X POST "${API_URL}/api/campaigns/${CAMPAIGN_ID}/pause" \
  -H "X-API-Key: ${API_KEY}"

Rate limits and scheduling

Campaign execution respects the schedule configuration to avoid overwhelming recipients and messaging providers.
SettingDescriptionDefault
schedule.rateLimitMaximum messages sent per minute5
schedule.activeHoursTime window for sending (requires start, end, timezone)All hours
schedule.activeDaysDays of the week (0=Sunday through 6=Saturday)All days
schedule.startAtScheduled start time (campaign waits until this time)Immediate
The rate limit applies per campaign. If you have multiple campaigns running simultaneously, each one sends at its own rate. Plan accordingly to stay within your messaging provider’s throughput limits.

Checking available channels

Before creating a campaign, verify which channels your account has configured:
curl "${API_URL}/api/campaigns/channels" \
  -H "X-API-Key: ${API_KEY}"
This returns only channels with verified integrations, for example ["whatsapp", "email"]. Attempting to create a campaign on an unconfigured channel will result in a validation error.

Campaign lifecycle

A campaign moves through these statuses:
StatusDescription
draftInitial state. Can be edited, launched, or deleted.
scheduledHas a future startAt time. Transitions to running automatically.
runningActively sending messages and processing conversations.
pausedHalted by the user. Can be relaunched.
completedAll leads have been processed. Terminal state.
Only draft campaigns can be edited or deleted. Once a campaign has been launched, create a new campaign if you need different settings.