> ## Documentation Index
> Fetch the complete documentation index at: https://docs.naturalead.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Launch Your First Campaign

> End-to-end guide to creating and launching an automated outreach campaign that qualifies leads using AI conversations.

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:

<CardGroup cols={3}>
  <Card title="AI Agent" icon="robot">
    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.
  </Card>

  <Card title="Messaging Integration" icon="plug">
    At least one verified channel integration (WhatsApp Business API (Meta), Telegram bot, or Resend for email). Configure integrations in **Settings > Integrations**.
  </Card>

  <Card title="Leads" icon="users">
    Leads in your account to reach out to. Import them via CSV, the Leads API, or a CRM sync webhook.
  </Card>
</CardGroup>

<Warning>
  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.
</Warning>

## Step-by-step walkthrough

<Steps>
  ### Choose and configure your AI agent

  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.

  If you already have an agent configured, note its `agentConfigId`. Otherwise, create one first:

  <CodeGroup>
    ```bash curl theme={null}
    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 Python theme={null}
    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"]
    ```

    ```javascript Node.js theme={null}
    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;
    ```
  </CodeGroup>

  ### Create the campaign

  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.

  <CodeGroup>
    ```bash curl theme={null}
    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 Python theme={null}
    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"]
    ```

    ```javascript Node.js theme={null}
    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;
    ```
  </CodeGroup>

  <Info>
    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.
  </Info>

  ### Preview matched leads

  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.

  <CodeGroup>
    ```bash curl theme={null}
    curl "${API_URL}/api/campaigns/${CAMPAIGN_ID}" \
      -H "X-API-Key: ${API_KEY}"
    ```

    ```python Python theme={null}
    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']}")
    ```

    ```javascript Node.js theme={null}
    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}`);
    ```
  </CodeGroup>

  If you need to adjust the campaign, update it while it is still in draft status:

  ```bash theme={null}
  curl -X PATCH "${API_URL}/api/campaigns/${CAMPAIGN_ID}" \
    -H "X-API-Key: ${API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{ "schedule": { "rateLimit": 5 } }'
  ```

  ### Launch the campaign

  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.

  <CodeGroup>
    ```bash curl theme={null}
    curl -X POST "${API_URL}/api/campaigns/${CAMPAIGN_ID}/launch" \
      -H "X-API-Key: ${API_KEY}"
    ```

    ```python Python theme={null}
    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"
    ```

    ```javascript Node.js theme={null}
    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"
    ```
  </CodeGroup>

  <Warning>
    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.
  </Warning>

  ### Monitor progress and stats

  Poll the campaign endpoint to track progress. The `stats` object updates in real time as the AI agent converses with leads.

  <CodeGroup>
    ```bash curl theme={null}
    curl "${API_URL}/api/campaigns/${CAMPAIGN_ID}" \
      -H "X-API-Key: ${API_KEY}" \
      | jq '.stats'
    ```

    ```python Python theme={null}
    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)
    ```

    ```javascript Node.js theme={null}
    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);
    ```
  </CodeGroup>

  To pause a running campaign and stop further outreach:

  ```bash theme={null}
  curl -X POST "${API_URL}/api/campaigns/${CAMPAIGN_ID}/pause" \
    -H "X-API-Key: ${API_KEY}"
  ```
</Steps>

## Rate limits and scheduling

Campaign execution respects the schedule configuration to avoid overwhelming recipients and messaging providers.

| Setting                | Description                                                   | Default   |
| ---------------------- | ------------------------------------------------------------- | --------- |
| `schedule.rateLimit`   | Maximum messages sent per minute                              | 5         |
| `schedule.activeHours` | Time window for sending (requires `start`, `end`, `timezone`) | All hours |
| `schedule.activeDays`  | Days of the week (0=Sunday through 6=Saturday)                | All days  |
| `schedule.startAt`     | Scheduled start time (campaign waits until this time)         | Immediate |

<Note>
  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.
</Note>

## Checking available channels

Before creating a campaign, verify which channels your account has configured:

```bash theme={null}
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:

| Status      | Description                                                        |
| ----------- | ------------------------------------------------------------------ |
| `draft`     | Initial state. Can be edited, launched, or deleted.                |
| `scheduled` | Has a future `startAt` time. Transitions to running automatically. |
| `running`   | Actively sending messages and processing conversations.            |
| `paused`    | Halted by the user. Can be relaunched.                             |
| `completed` | All leads have been processed. Terminal state.                     |

<Info>
  Only **draft** campaigns can be edited or deleted. Once a campaign has been launched, create a new campaign if you need different settings.
</Info>
