Skip to content
BuildBase

Schedule, pause and A/B test campaigns

Schedule a campaign, pass the pre-send check, send a new list to a sample first, A/B test the subject, and pause, resume or cancel mid-send.

A campaign is one email sent to a whole list, such as a newsletter or a launch announcement. This page covers what happens between pressing Send and the last message going out: the checks, scheduling, testing two subject lines, and stopping a send halfway.

The checks are the same whether you press Send in the console or call the API. A pre-send check refuses mail that would break anti-spam law or get cut off by Gmail. A list you have never mailed goes to a small sample first. And before every batch, a health check pauses the campaign if too many messages bounce (come back undelivered).

# Check, then schedule for tomorrow morning
curl https://api.console.buildbase.app/api/emails/campaigns/$CAMPAIGN_ID/presend-check \
  -H "Authorization: $BUILDBASE_TOKEN"

curl -X POST https://api.console.buildbase.app/api/emails/campaigns/$CAMPAIGN_ID/schedule \
  -H "Authorization: $BUILDBASE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scheduledAt":"2026-11-02T09:00:00Z"}'

Before you start

A campaign needs a template, an audience list and a sender with an active account (a Mailgun sender also on a verified sending domain). Build it in the console under Messaging > Campaigns.

The pre-send check

Before anything goes out, BuildBase looks for problems that would get the campaign blocked, clipped or reported. Some of them stop the send. The rest are advice.

GET /api/emails/campaigns/:id/presend-check answers with both lists, and the console shows them above the Send button. /send and /schedule refuse a campaign with anything in the first list. They answer 422 with the code EMAIL_PRESEND_FAILED, so an API caller cannot skip the check.

{{unsubscribe}} and {{manage-preference}} below are merge tags: placeholders BuildBase fills in with each recipient's own link.

Blocks the sendWhy
No subject, or no contentNothing to deliver
No {{unsubscribe}} or {{manage-preference}}Marketing mail with no way out breaks anti-spam law and Gmail and Yahoo's bulk sender rules
HTML over 100 KBGmail cuts a message off at about 102 KB and hides the rest, unsubscribe link included
A/B test on, with no second subjectThere is nothing to test
No active, verified senderThe mail has no way to leave
No postal address ({{companyAddress}}), or the tag has no valueAnti-spam law in the US, Canada and the EU requires one in every marketing email
Only adviceWhy
A subject over 70 charactersInboxes cut it off
No preview textInboxes show the first words of the body instead
An A/B test on a list under 200Too small to tell two subjects apart; everyone gets subject A

The response also carries htmlBytes: the size of the HTML before BuildBase adds its open and click tracking.

Schedule

Pick a time and the campaign goes out on its own.

POST /:id/schedule takes a scheduledAt at least a minute ahead and no more than a year out. It runs the checks /send runs at the moment you schedule: the pre-send check, a working sender, and whether your sending is suspended. So a campaign that could not go out is refused now, not at 3am. When the time comes, the marketing block and a suspension are checked again.

A draft or failed campaign can be scheduled, and a scheduled one moved to a new time. It moves to scheduled and starts on its own. POST /:id/unschedule puts it back to draft.

A new list goes to a sample first

The first time your organization mails a list of 1,000 or more, the campaign goes to a sample and holds the rest. Old exports and bought lists often hold dead addresses. A sample shows that before every address on the list has been mailed.

SettingValue
Sample size5% of the list, at least 200 and at most 2,000
Which peopleBatches spread across the whole list, not the first rows
The wait3 hours, for bounces and most spam complaints to come back
The releaseAutomatic after the wait, if bounces and complaints are low

Why spread it out? A list is read oldest member first, and the oldest addresses are the likeliest to bounce, so a sample from the top would test only those. A list your organization has already finished a campaign to is not sampled again.

POST /:id/release-sample sends to the rest now instead of waiting. It skips the wait, never the health check: a sample that is bouncing stays held, and the answer says why. To turn the sample off for one campaign, set sampleFirst: false in the campaign's PATCH /api/emails/campaigns/:id body, which also needs its name, subject and sender.

A/B test the subject

An A/B test sends two subject lines to a slice of your list, waits, and sends the one that did better to everyone else. Set abTest in the campaign's PATCH /api/emails/campaigns/:id body, next to its name, subject and sender (the update needs all three):

{
  "abTest": {
    "enabled": true,
    "subjectB": "Your November product notes",
    "testPercent": 20,
    "waitHours": 4,
    "metric": "opens"
  }
}
FieldWhat it setsRangeDefault
testPercentHow much of the list is in the test10 to 5020
waitHoursHow long to wait before picking1 to 244
metricWhat decides the winneropens, clicksopens

The test share goes out in batches that alternate between A and B along the list, at least two batches per subject. After the wait, BuildBase compares the unique open (or click) rate of each subject, counting only messages that reached a mailbox. The higher one goes to everyone else. A tie, or no data, keeps subject A. A list under 200 recipients sends subject A to everyone.

Pause, resume, cancel

You can stop a campaign partway through.

EndpointWhat it does
POST /:id/pauseHolds the batches not yet dispatched and the messages already queued
POST /:id/resumeRe-queues what was held
POST /:id/cancelDrops whatever has not gone out. Those recipients never get it

Only a campaign that is sending can be paused, only a paused one resumed, and only one that is sending or paused cancelled. A sent campaign whose messages are still queued (messagesWaiting on GET /:id) can also be paused or cancelled. Anything else answers 409.

The health check can also pause a campaign by itself. Before each batch, it compares the campaign's bounce and complaint rates with the pause level in Deliverability. A complaint is a recipient pressing Report spam. Once it pauses, someone looks at the list and resumes it. After that, only the organization-wide block stops the campaign again.

Retry the failures

POST /:id/retry-failed re-queues only the recipients whose message failed, never those who already received it, so it is safe to call more than once. It answers with the counts it acted on:

{
  "message": "Retried 12 failed emails, 3 skipped",
  "retried": 12,
  "skipped": 3
}

Publish the web version

Once a campaign has gone out, you can publish it as a web page, so people can read it in a browser and you can link to it.

POST /:id/archive with { "archive": "public" } publishes it and answers with its URL on the console's address, https://console.buildbase.app on BuildBase's cloud. The page shows the campaign as sent, with every merge tag left empty and no tracking, so no recipient's details appear on it. { "archive": "off" } withdraws it. Campaigns sent under a public unsubscribe group can be listed together in a newsletter archive.

Reports

Each campaign has a report. It counts messages delivered, pending, failed, bounced and skipped, unique and total opens and clicks, spam reports and unsubscribes.

Delivery is also broken down by mailbox provider, such as Gmail. If one provider starts turning your mail away, it shows on its own row. The Gmail row carries Gmail's own spam rate for the sending domain when the domain is connected to Gmail Postmaster.

When a send is refused

A refused send answers with an HTTP status and a code your code can branch on.

StatusCodeMeaning
422EMAIL_PRESEND_FAILEDThe pre-send check found a blocking problem; problems lists them
409EMAIL_MARKETING_BLOCKEDMarketing mail is blocked for the organization's bounce or complaint rate
403SENDING_SUSPENDEDBuildBase suspended the organization's sending; contact support

Next Steps