Campaigns

Creating Campaigns

Campaigns are multi-step cold email sequences that automatically send to your leads based on schedules, conditions, and mailbox rotation. This guide walks you through creating your first campaign.

Prerequisites

Creating a Campaign (Dashboard)

1

Navigate to Campaigns

From the dashboard sidebar, click CampaignsNew Campaign.

2

Configure campaign settings

Set the following:

  • Name — Internal name for your reference
  • Timezone — Used for send window enforcement (default: UTC)
  • Send Window — Start/end hours (e.g., 09:00 – 17:00)
  • Send Days — Which days to send (e.g., Monday–Friday = 1,2,3,4,5)
  • AI Auto-Reply — Toggle ON to auto-respond when leads reply
  • AI Context — Prompt telling the AI about your product/goals
  • Scheduled Start — Optional future date/time to begin sending
3

Add sequence steps

Add one or more email steps. Each step supports:

  • Subject — Primary subject line (with personalization variables)
  • Subject Variants — Additional subject lines for A/B rotation
  • Body — HTML email content with variables and spintax
  • Body Variants — Additional body versions for rotation
  • Delay Days / Hours — Wait time before this step (after previous)
  • Condition — When to send: not_replied, replied, not_opened, opened
  • Is Reply — Thread this step as a reply (adds Re: prefix, In-Reply-To header)
4

Assign mailboxes

Select the mailbox accounts to use for sending. The dispatcher rotates between them using round-robin — picks the mailbox with the lowest sent_today that's under its daily limit.

5

Add leads to the campaign

Assign leads from your uploaded lists to this campaign. Only leads that are not bounced, unsubscribed, or blocklisted will receive messages.

6

Start the campaign

Click Start to activate. The dispatcher will begin sending when the next send window opens (or immediately if within window). Requirements to start:

  • At least 1 step must exist
  • At least 1 mailbox must be assigned

Creating via API

bash
# Step 1: Create the campaign
curl -X POST https://YOUR_SERVER/api/v1/workspaces/1/campaigns \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=YOUR_JWT_TOKEN" \
  -d '{
    "name": "Q2 Outreach",
    "timezone": "America/New_York",
    "send_window_start": "09:00",
    "send_window_end": "17:00",
    "send_days": "1,2,3,4,5",
    "ai_auto_reply": true,
    "ai_context": "We sell project management software. Be helpful and concise."
  }'

# Step 2: Add a step
curl -X POST https://YOUR_SERVER/api/v1/workspaces/1/campaigns/CAMPAIGN_ID/steps \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=YOUR_JWT_TOKEN" \
  -d '{
    "step_type": "email",
    "subject": "Hey {{first_name}}, quick question",
    "subject_variants": ["{{first_name}} — saw your work at {{company}}"],
    "body": "<p>Hi {{first_name}},</p><p>{I noticed|I saw} your work at {{company}}...</p>",
    "delay_days": 0,
    "condition": "not_replied"
  }'

# Step 3: Assign mailboxes
curl -X POST https://YOUR_SERVER/api/v1/workspaces/1/campaigns/CAMPAIGN_ID/mailboxes \
  -H "Content-Type: application/json" \
  -H "Cookie: auth_token=YOUR_JWT_TOKEN" \
  -d '{"mailbox_ids": ["MAILBOX_UUID_1", "MAILBOX_UUID_2"]}'

# Step 4: Start
curl -X POST https://YOUR_SERVER/api/v1/workspaces/1/campaigns/CAMPAIGN_ID/start \
  -H "Cookie: auth_token=YOUR_JWT_TOKEN"

Campaign Lifecycle

StatusBehavior
draftNot processing — safe to edit steps and settings
activeDispatcher is sending to leads per schedule
pausedStopped — can be resumed, leads retain their position
Auto-pause on high bounce rate

If hard bounce rate exceeds 5% (after 100+ sends), the campaign is automatically paused to protect sender reputation. Fix your list quality before resuming.

How the Dispatcher Works

The campaign dispatcher runs as a periodic Asynq task and:

  1. Finds leads with next_send_at <= now (uses FOR UPDATE SKIP LOCKED to prevent races)
  2. Checks campaign is active and within its send window (timezone-aware)
  3. Checks scheduled start time (skips if not yet time)
  4. Picks the mailbox with lowest sent_today under its effective limit
  5. Checks blocklist (email + domain level)
  6. Picks subject/body variant (deterministic based on lead email hash)
  7. Renders spintax + variable substitution
  8. Builds threading headers for follow-ups (In-Reply-To, References)
  9. Injects tracking pixel + unsubscribe link
  10. Sends via SMTP with human-mimicry delay (30–90 seconds between sends)
  11. On success: advances lead to next step, updates stats
  12. On hard bounce: blocklists lead, pauses campaign if rate > 5%
  13. On soft bounce: retries in 4 hours