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

# Campaigns

> Send targeted email broadcasts to your subscriber segments

# Campaigns

Campaigns are one-time email broadcasts sent to a segment of your subscribers. Use them for announcements, newsletters, promotions, and any communication that goes to multiple people at once.

Workspaces with the [SMS add-on](/concepts/sms) can also send SMS campaigns: the Campaigns screen gains an Email / SMS switch, and text campaigns follow the same message, recipients, schedule, and review steps. See [SMS campaigns](/concepts/sms#sms-campaigns) for the SMS-specific rules.

## Campaigns vs Sequences

| Aspect         | Campaigns                  | Sequences              |
| -------------- | -------------------------- | ---------------------- |
| **Timing**     | One-time, scheduled        | Ongoing, triggered     |
| **Trigger**    | Manual or scheduled        | Automatic (tag/event)  |
| **Recipients** | Filtered segment           | Individual subscribers |
| **Use case**   | Newsletters, announcements | Onboarding, follow-ups |

## Campaign States

```
┌─────────┐     ┌───────────┐     ┌─────────┐     ┌────────┐
│  Draft  │ ──▶ │ Scheduled │ ──▶ │ Sending │ ──▶ │  Sent  │
└─────────┘     └───────────┘     └─────────┘     └────────┘
     │                │                │
     │                ▼                ▼
     │         ┌───────────┐    ┌──────────┐
     │         │ Cancelled │    │  Paused  │
     │         └───────────┘    └──────────┘
     │
     ▼  (safety review, see below)
┌──────────────────┐     ┌───────────┐
│ Waiting approval │ ──▶ │  Rejected │
└──────────────────┘     └───────────┘
```

| State              | Description                                                                   |
| ------------------ | ----------------------------------------------------------------------------- |
| `draft`            | Being created or edited                                                       |
| `scheduled`        | Queued to send at a specific time                                             |
| `waiting_approval` | Held for [safety review](#safety-review) before it can be queued              |
| `rejected`         | Safety review declined the send; the reason is returned in `rejectionComment` |
| `sending`          | Currently being sent                                                          |
| `paused`           | Sending was paused manually or by a company-level sending lock                |
| `sent`             | Completed sending                                                             |
| `cancelled`        | Manually cancelled before sending                                             |

## Safety Review

Scheduling a campaign can put it in `waiting_approval` instead of `scheduled`.
This is an anti-abuse hold, not an error: nothing is sent while a campaign is in
this state, and the campaign is not lost.

**This most often happens on new accounts.** The checks run against your account
and your sending domain, so a brand-new workspace or a recently registered
domain is the usual reason a first send is held. Once your account and domain
have a track record, later sends normally go straight to `scheduled`.

A campaign is held when any of these apply:

* **New or unverifiable domain.** Your sending domain or your company website
  domain was registered in the last 7 days, has no public registration record,
  or the website did not respond when we checked it.
* **Content that matches known abuse patterns.** Business-opportunity, guaranteed
  income, or high-pressure "act now" language in the subject, preheader, or body.
* **Automated content review.** Campaigns to more than 10 recipients, or
  campaigns whose content hits our review watchlist, are checked automatically
  before the first send.

Established accounts that our team has marked as trusted skip these checks
entirely.

### What happens next

Held campaigns are reviewed by the Sequenzy team. When a review finishes:

* **Approved** - the campaign returns to `scheduled` and sends at its original
  time. If that time has already passed while it waited, it moves straight to
  `sending`.
* **Rejected** - the campaign moves to `rejected` and the reason is available in
  `rejectionComment` on `GET /api/v1/campaigns/{campaignId}`, `sequenzy campaigns get <id>`, and the `get_campaign` MCP tool.

The hold is evaluated per schedule call, not stored permanently on the campaign.
Fixing the underlying cause (verifying a domain that now has history, rewriting
flagged copy) and scheduling again is enough - you do not need a per-campaign
exemption.

<Note>
  If you are building an unattended integration, treat `waiting_approval` as a
  normal outcome of scheduling. Check `status` in the schedule response, and if
  it is `waiting_approval`, log it and poll the campaign instead of retrying the
  schedule call. Retrying will not clear the hold.
</Note>

If your account sends automated campaigns and you keep hitting review, contact
support and ask to have the workspace marked as trusted. There is no API
parameter or dashboard setting that bypasses the review.

## Creating a Campaign

### 1. Choose Recipients

Define who receives the campaign:

**All Subscribers**

```
Recipients: All active subscribers
Filters: None
```

**Filtered Segment**

```
Recipients: Filtered
Filters:
  - Status is "active"
  - Tags contains "customer"
  - Tags not_contains "churned"
```

### 2. Select Email Template

Choose or create the email content:

* Use an existing template from your library
* Create new content in the editor
* Duplicate and modify a previous campaign

You can also make individual blocks conditional. In the block settings, use **Conditional Display** to show that block only when a recipient value matches a rule such as `equals`, `contains`, `exists`, or numeric comparisons.

Conditions are not limited to profile values: you can also target segment membership, custom events (for example "performed `purchase` in the last 30 days"), tags, lists, subscription status, engagement (opened or clicked recently), Stripe and store purchases, signup date, email provider, and SMS status - the same filters you use to build segments. These rules are checked against live subscriber data when each email is sent.

For an `else` branch, use the Conditional (if/else) block, or duplicate the block and invert the rule on the copy. Attribute and variable conditions use the same merge-tag variable names you already use in emails, but without the `{{ }}` braces.

### 3. Choose Sender

Select who the email appears to come from:

```
From: "Sequenzy Team" <hello@sequenzy.com>
```

### 4. Schedule or Send

* **Send Now**: Queue immediately
* **Schedule**: Pick a specific date and time
* **Spread Over Time**: Distribute sends linearly over 1–72 hours

You now choose these in two layers:

1. **Start time**: Send now or schedule a future start
2. **Delivery strategy**: All at once, Spread Over Time, or Send Time Optimization

### Recurring Campaigns

Scheduled campaigns can repeat automatically. When scheduling, set **Repeat**
to "Every week" or "Every month" and the campaign becomes a recurring
template:

* At each run time, Sequenzy duplicates the template into a normal one-shot
  campaign and sends it
* The audience is re-evaluated at every run, so new subscribers who match the
  list or segment are included and removed ones are skipped
* Each run gets its own campaign (named after the template with the run date)
  with its own stats
* The email content is snapshotted per run - you can edit the template between
  runs, and the next run picks up your changes without altering past runs
* Cancel the recurring campaign to stop the series; runs already sent are
  unaffected

Recurring campaigns pair well with dynamic product lists: a repeat block with
**Recommended products** and **Rotate between sends** turned on sends each
subscriber a fresh selection every month without repeating what they already
saw.

<Note>
  If the platform is briefly unavailable at a run time, the run starts as soon
  as it recovers. Runs missed by more than the full interval are skipped, not
  sent in a burst.
</Note>

### 5. Spread Over Time (Optional)

Enable Spread Over Time to distribute your campaign sends gradually over a configurable window, protecting your sender reputation during warmup.

**How it works:**

1. Choose when the campaign should start sending
2. Select "Spread over time" as the delivery strategy
3. Use the slider to choose a delivery window (1-72 hours)
4. The first batch sends at the chosen start time
5. Remaining batches are spaced evenly across the window

**When to use:**

* Warming up a new sending domain
* Sending your first large campaign (1,000+ recipients)
* You've sent fewer than 10,000 emails total (Sequenzy will recommend this automatically)

**Example:**

```
Campaign start: Tomorrow at 9:00 AM
Spread window: 12 hours
Subscribers: 1,200
Chunks: 5

Chunk 1: Sent at 9:00 AM
Chunk 2: Sent at 12:00 PM
Chunk 3: Sent at 3:00 PM
Chunk 4: Sent at 6:00 PM
Chunk 5: Sent at 9:00 PM
```

<Note>
  Spread Over Time and Send Time Optimization are mutually exclusive. You can
  only enable one per campaign.
</Note>

### Pauses and Sending Locks

A manual campaign pause stores the pause on that campaign. You can resume it
from the campaign page, and Sequenzy uses the saved spread setting unless you
choose a new one on resume.

Company sending pauses and real account bans are different. They act as
parent sending locks and do not rewrite every campaign row. Running or due
scheduled campaigns show **Sending paused** while the lock is active. When the
lock is removed, campaigns that are still sending or already due continue
through the normal resume worker; future scheduled campaigns wait for their
scheduled start.

If you do not want a campaign to continue after company sending is restored,
pause that campaign manually. The manual pause remains until you resume it.

### 6. Send Time Optimization (Optional)

Enable Send Time Optimization to deliver emails at each subscriber's optimal time based on their engagement patterns.

**How it works:**

1. Toggle "Send Time Optimization" on the schedule step
2. Sequenzy analyzes each subscriber's historical open times (last 90 days)
3. Emails are delivered within a 12-hour window at each subscriber's optimal hour
4. Subscribers without enough data receive emails at the scheduled time

**Requirements:**

* Subscribers need at least 5 email opens in the last 90 days
* The UI shows how many subscribers have computed optimal times

**Example:**

```
Campaign scheduled: 9:00 AM
12-hour window: 9:00 AM - 9:00 PM

Subscriber A: Usually opens at 2:00 PM → Sent at 2:00 PM
Subscriber B: Usually opens at 6:00 PM → Sent at 6:00 PM
Subscriber C: Not enough data → Sent at 9:00 AM (scheduled time)
```

After sending, view the delivery distribution chart to see how STO spread your campaign across hours.

## Filtering Options

### Status Filters

| Filter              | Description               |
| ------------------- | ------------------------- |
| `is subscribed`     | Only active subscribers   |
| `is_not subscribed` | Exclude active (rare use) |

### Tag Filters

| Filter               | Description           |
| -------------------- | --------------------- |
| `contains [tag]`     | Has the specified tag |
| `not_contains [tag]` | Does not have the tag |

**Example: Target customers, exclude churned:**

```
Tags contains "customer"
Tags not_contains "churned"
Tags not_contains "unsubscribed"
```

### Email Filters

| Filter                | Description                        |
| --------------------- | ---------------------------------- |
| `contains [text]`     | Email contains text                |
| `not_contains [text]` | Email does not contain text        |
| `is [address]`        | Email is exactly this address      |
| `is_not [address]`    | Email is anything but this address |

`is` and `is_not` match the full address and ignore case, so they are the right
choice for explicit allowlists (QA contacts) and exclusions. Use `contains` when
you want a domain or substring.

**Example: Target company emails:**

```
Email contains "@company.com"
```

**Example: Only a fixed list of test contacts:**

```
Email is "qa1@company.com" OR Email is "qa2@company.com"
```

### Date Filters

| Filter              | Description                |
| ------------------- | -------------------------- |
| `less_than [value]` | Added within/after a value |
| `more_than [value]` | Added before/older than it |

You can use rolling values like `7 days ago`, natural language like `last week`, or fixed dates like `2026-03-01` and `Mar 1 2026`.

Rolling values update automatically as time passes. Fixed dates stay fixed.

**Examples:**

```
Added less_than 30 days ago
Added more_than 2026-03-01
```

**More targeting examples:**

```
Status is "active"
Added less_than 7 days ago
Tags contains "customer"
```

```
Tags contains "trial"
Added more_than Mar 1 2026
```

```
Attribute trial_ends_at lte 3 days from now
Email contains "@company.com"
```

### Name Filters

| Filter                      | Description         |
| --------------------------- | ------------------- |
| `firstName contains [text]` | First name contains |
| `lastName contains [text]`  | Last name contains  |

## Filter Combinations

Filters are combined with AND logic:

```
Tags contains "customer"
  AND
Tags contains "pro-plan"
  AND
Added more_than 7 days ago
```

This targets: Pro plan customers who signed up more than a week ago.

## Sending Process

When a campaign is sent:

```
1. Campaign marked as "sending"
         │
         ▼
2. Fetch all matching subscribers
   (apply filters)
         │
         ▼
3. Split into chunks (100 per batch)
         │
         ▼
4. Queue each chunk for processing
         │
         ▼
5. Each chunk:
   - Render email with personalization
   - Send via email provider
   - Record in analytics
         │
         ▼
6. All chunks complete
         │
         ▼
7. Campaign marked as "sent"
```

## Personalization

### Available Variables

Use these in your email content:

| Variable              | Description             |
| --------------------- | ----------------------- |
| `{{FIRST_NAME}}`      | Subscriber's first name |
| `{{LAST_NAME}}`       | Subscriber's last name  |
| `{{NAME}}`            | Full name               |
| `{{EMAIL}}`           | Email address           |
| `{{customAttribute}}` | Any custom attribute    |

### Example

```html theme={null}
<p>Hi {{FIRST_NAME|there}},</p>

<p>As a {{plan}} customer, we wanted to share...</p>

<p>Your account: {{EMAIL}}</p>
```

The `|there` syntax provides a fallback if the name is missing.

## Tracking

### Open Tracking

When enabled, Sequenzy tracks email opens via a tracking pixel.

```html theme={null}
<!-- Automatically inserted -->
<img src="https://api.sequenzy.com/t/o/[tracking-id].gif" />
```

### Click Tracking

When enabled, links are wrapped to track clicks:

```html theme={null}
<!-- Original -->
<a href="https://example.com">Click here</a>

<!-- With tracking -->
<a href="https://api.sequenzy.com/t/c/[tracking-id]?url=...">Click here</a>
```

<Note>You can enable/disable tracking per company in Settings → Tracking.</Note>

## Analytics

After sending, view campaign performance:

| Metric           | Description               |
| ---------------- | ------------------------- |
| **Sent**         | Total emails sent         |
| **Delivered**    | Successfully delivered    |
| **Bounced**      | Failed to deliver         |
| **Opens**        | Unique opens              |
| **Clicks**       | Unique clicks             |
| **Unsubscribes** | Opted out from this email |
| **Open Rate**    | Opens / Delivered         |
| **Click Rate**   | Clicks / Delivered        |

### Revenue Attribution

If you have [goals](/concepts/goals) configured, sent campaigns show revenue attribution metrics:

| Metric          | Description                                     |
| --------------- | ----------------------------------------------- |
| **Conversions** | Goal conversions attributed to this campaign    |
| **Revenue**     | Total tracked value from attributed conversions |

Attribution uses a 24-hour window from email interaction to conversion. Works automatically with Stripe, Polar, Paddle, and Dodo integrations.

### Tracking Over Time

View how engagement changes:

```
Day 1: 45% opens, 12% clicks
Day 2: 52% opens, 15% clicks
Day 7: 58% opens, 17% clicks
```

Most engagement happens in the first 48 hours.

## Resending to Non-Openers

After a campaign finishes sending, you can resend it to everyone in the same audience who didn't open it. This is a simple way to recover opens - a second touch, often with a fresh subject line, reaches the people who missed the first send.

On a sent campaign, use the **Send to non-openers** button. Before creating anything, it shows an estimate of how many subscribers haven't opened the campaign yet, so you know the size of the resend up front.

Confirming creates a new **draft** named `<campaign> (Non-openers)` that:

* Reuses the original campaign's audience (all subscribers, lists, a saved segment, or a filtered audience), and
* Adds a "didn't open this campaign" rule so only people who haven't opened it yet are targeted.

The draft is **not** sent automatically. Review it, tweak the subject line, then schedule or send it like any other campaign. Manual exclusions from the original send are preserved; manually added recipients are dropped so they don't bypass the rule.

<Note>
  Resending to non-openers becomes available **6 hours after a campaign finishes
  sending**. This gives opens time to register, so the estimate and the resend
  audience are accurate.
</Note>

This is also available outside the dashboard:

* **API**: `POST /api/v1/campaigns/{campaignId}/resend-to-non-openers` (see the [API reference](/api-reference/campaigns/resend-to-non-openers))
* **CLI**: `sequenzy campaigns resend-to-non-openers <id>`
* **MCP**: the `resend_campaign_to_non_openers` tool

## Campaign Types

### Newsletter

Regular updates to your entire list:

```
Recipients: All active subscribers
Tags: contains "newsletter"
Schedule: Every Tuesday, 10am
```

### Product Announcement

New feature or product launch:

```
Recipients: Filtered
Tags: contains "customer"
Send: Immediately
```

### Promotional

Sales, discounts, special offers:

```
Recipients: Filtered
Tags: contains "active"
Tags: not_contains "enterprise" (they don't need discounts)
```

### Re-engagement

Bring back inactive subscribers:

```
Recipients: Filtered
Tags: contains "inactive"
Added: more_than 60 days ago
```

### Segmented Updates

Different content for different segments:

```
Campaign A: Pro Plan Updates
  - Tags: contains "pro-plan"

Campaign B: Starter Plan Updates
  - Tags: contains "starter-plan"

Campaign C: Free Tier Updates
  - Tags: contains "free-tier"
```

## Best Practices

### 1. Test Before Sending

* Send a test email to yourself
* Check personalization renders correctly
* Verify links work

### 2. Segment Thoughtfully

Don't send everything to everyone:

```
✓ Feature update → Only to customers using that feature
✓ Upgrade offer → Only to free/starter tiers
✓ Renewal reminder → Only to annual subscribers

✗ Everything → All subscribers
```

### 3. Time Your Sends

* **B2B**: Tuesday-Thursday, 10am-2pm
* **B2C**: Evenings and weekends
* **Consider time zones**: Schedule for recipient's local time

### 4. Monitor Deliverability

Watch your metrics:

```
⚠ Bounce rate > 5%  → Clean your list
⚠ Complaint rate > 0.1%  → Review targeting
⚠ Open rate < 10%  → Improve subject lines
```

### 5. Respect Unsubscribes

* Process unsubscribes immediately
* Don't re-add unsubscribed contacts
* Provide easy unsubscribe links

## Campaigns vs Transactional

| Use Campaign       | Use Transactional   |
| ------------------ | ------------------- |
| Marketing messages | Order confirmations |
| Newsletters        | Password resets     |
| Promotions         | Account alerts      |
| Announcements      | Receipts            |

<Warning>
  Don't use campaigns for transactional messages—they may be delayed or
  filtered.
</Warning>

## Related

<CardGroup cols={2}>
  <Card title="Sequences" icon="diagram-project" href="/concepts/sequences">
    Automated multi-step workflows
  </Card>

  <Card title="Subscribers" icon="users" href="/concepts/subscribers">
    Managing your contact list
  </Card>

  <Card title="Tags" icon="tags" href="/concepts/tags">
    Segmenting for targeting
  </Card>

  <Card title="Transactional" icon="code" href="/concepts/transactional-emails">
    Per-user triggered emails
  </Card>
</CardGroup>
