# Campaign Follow-ups & Controls

> Engagement follow-ups, mailing-list To, and the unsubscribe policy on POST /campaigns.

`POST /campaigns`

Three optional controls extend a campaign beyond the base fields.

## Engagement follow-ups

Up to 5 `followups` send automatically after the campaign finishes — each fires `delay` later to the recipients matching its `condition`, threaded as a reply to their original email (same conversation; subject defaults to `Re: <campaign subject>`). Bodies support the same merge tags as the campaign.

| Field | Description |
| --- | --- |
| `condition` | One of `opened`, `clicked`, `not_opened`, `not_clicked`, `replied`, `not_replied`. |
| `delay` | Natural-language duration after the send completes, e.g. `"5 hours"`, `"4 days"` (max 30 days). |
| `subject` | Optional; defaults to `Re: <campaign subject>` to keep the thread. |
| `html` | The follow-up body (required). |

> **Note:** Open/click conditions require tracking to be enabled on the sending domain.

## Mailing-list To

Set `list_to: true` and every message shows a generated `recipient-<hex>@your-domain` address as the visible To header. Delivery is unchanged — each mail is still sent individually to its real recipient.

## Unsubscribe policy

| `unsubscribe_policy` | Behavior |
| --- | --- |
| `account` (default) | Any opt-out blocks the recipient — today's behavior. |
| `domain` | Only opt-outs recorded for THIS sending domain (plus account-wide ones) block. An unsubscribe from one of your other domains does not. |
| `ignore` | The unsubscribe list is skipped. Hard-bounced and complained addresses are ALWAYS excluded regardless. |

> **Warning:** Use `ignore` only where you have a lawful basis to email recipients who opted out.

## A/B winning metric

The `ab_test.metric` now accepts `reply` in addition to `open` and `click` — the winner is the variant with the higher reply rate (replies are matched from your inbound mail).

**Node.js**

```js
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr('mb_xxxxxxxxx');

const { data, error } = await mb.campaigns.create({
  "domain": "yourdomain.com", "from": "You <you@yourdomain.com>",
  "subject": "Big news", "html": "<p>Hi {{name}} …</p>",
  "list_to": true,
  "unsubscribe_policy": "domain",
  "ab_test": { "enabled": true, "subject_b": "Bigger news", "test_pct": 20, "metric": "reply" },
  "followups": [
    { "condition": "not_opened", "delay": "5 hours", "html": "<p>Bumping this…</p>" }
  ]
});
console.log({ data, error });
```

**Ruby**

```ruby
require "mailblastr"

Mailblastr.api_key = "mb_xxxxxxxxx"

Mailblastr::Campaigns.create({
  "domain": "yourdomain.com",
  "from": "You <you@yourdomain.com>",
  "subject": "Big news",
  "html": "<p>Hi {{name}} …</p>",
  "list_to": true,
  "unsubscribe_policy": "domain",
  "ab_test": {
    "enabled": true,
    "subject_b": "Bigger news",
    "test_pct": 20,
    "metric": "reply"
  },
  "followups": [
    {
      "condition": "not_opened",
      "delay": "5 hours",
      "html": "<p>Bumping this…</p>"
    }
  ]
})
```

**PHP**

```php
$mailblastr = Mailblastr::client('mb_xxxxxxxxx');

$mailblastr->campaigns->create([
  'domain' => "yourdomain.com",
  'from' => "You <you@yourdomain.com>",
  'subject' => "Big news",
  'html' => "<p>Hi {{name}} …</p>",
  'list_to' => true,
  'unsubscribe_policy' => "domain",
  'ab_test' => [
    'enabled' => true,
    'subject_b' => "Bigger news",
    'test_pct' => 20,
    'metric' => "reply"
  ],
  'followups' => [
    [
      'condition' => "not_opened",
      'delay' => "5 hours",
      'html' => "<p>Bumping this…</p>"
    ]
  ]
]);
```

**Python**

```python
import mailblastr

mailblastr.api_key = "mb_xxxxxxxxx"

mailblastr.Campaigns.create({
  "domain": "yourdomain.com",
  "from": "You <you@yourdomain.com>",
  "subject": "Big news",
  "html": "<p>Hi {{name}} …</p>",
  "list_to": True,
  "unsubscribe_policy": "domain",
  "ab_test": {
    "enabled": True,
    "subject_b": "Bigger news",
    "test_pct": 20,
    "metric": "reply"
  },
  "followups": [
    {
      "condition": "not_opened",
      "delay": "5 hours",
      "html": "<p>Bumping this…</p>"
    }
  ]
})
```

**Go**

```go
client := mailblastr.NewClient("mb_xxxxxxxxx")

params := &mailblastr.CreateCampaignRequest{
    Domain:  "yourdomain.com",
    From:    "Acme <hello@yourdomain.com>",
    Subject: "Product update",
    Html:    "<p>Big news!</p>",
}
campaign, err := client.Campaigns.Create(params)
```

**Rust**

```rust
let mb = Mailblastr::new("mb_xxxxxxxxx");

let campaign = CreateCampaignOptions::new(
    "yourdomain.com",
    "Acme <hello@yourdomain.com>",
    "Product update",
).with_html("<p>Big news!</p>");
let _created = mb.campaigns.create(campaign).await?;
```

**Java**

```java
Mailblastr mailblastr = new Mailblastr("mb_xxxxxxxxx");

CreateCampaignRequest request = CreateCampaignRequest.builder()
    .domain("yourdomain.com")
    .from("Acme <hello@yourdomain.com>")
    .subject("Product update")
    .html("<p>Big news!</p>")
    .build();
mailblastr.campaigns().create(request);
```

**.NET**

```csharp
IMailblastr mailblastr = MailblastrClient.Create("mb_xxxxxxxxx");

var resp = await mailblastr.CampaignCreateAsync(new CampaignCreateOptions
{
    Domain = "yourdomain.com",
    From = "Acme <hello@yourdomain.com>",
    Subject = "Product update",
    HtmlBody = "<p>Big news!</p>",
});
```

**cURL**

```bash
curl -X POST 'https://api.mailblastr.com/campaigns' \
  -H 'Authorization: Bearer mb_xxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{
  "domain": "yourdomain.com", "from": "You <you@yourdomain.com>",
  "subject": "Big news", "html": "<p>Hi {{name}} …</p>",
  "list_to": true,
  "unsubscribe_policy": "domain",
  "ab_test": { "enabled": true, "subject_b": "Bigger news", "test_pct": 20, "metric": "reply" },
  "followups": [
    { "condition": "not_opened", "delay": "5 hours", "html": "<p>Bumping this…</p>" }
  ]
}'
```
