Campaigns

A campaign sends a marketing template from a verified domain to an audience and produces one message per eligible recipient. POST /campaigns creates either a blast — a one-time send to an audience built from lists, segments and uploaded files — or an automation, a standing journey that contacts enter one at a time (see Automations). Both are created in draft. This page covers blasts and the lifecycle both types share. The third type value, transactional, is refused with 422 VALIDATION_ERROR and validation_error: [{ "field": "type", "message": "NOT_IMPLEMENTED" }]; transactional mail is sent with POST /transactional/send.

Key fields

Field Description
type blast or automation.
status See Statuses.
domain_id The sending domain. It must be verified when you schedule or launch the campaign.
template_id / template_version The template must have message_class: marketing (otherwise 422 template_class_mismatch) and be active when you schedule or launch. A blast pins the template’s current version at launch, so later edits do not change what it sends; an automation uses the template’s current version at each send step.
audience Who receives a blast. See Audience. Not used by automations.
schedule_at The planned launch time of a scheduled blast; null otherwise.
sender_profile_id The sender profile whose mailbox is the From address. null uses the route for the campaign’s type (blast or automation) and, without a route, your default profile. An id that is not yours is 422 sender_not_found.
from_name / reply_to Optional overrides of the resolved profile’s display name and Reply-To. The From address itself always comes from a sender profile; the older from_email field is no longer accepted (422).
throttle_per_hour An optional cap on how many recipients per hour the campaign hands to sending. It paces the campaign only; the domain’s warmup allowance and other sending limits still apply.
recipient_count The number of recipients resolved at launch.
stats Counters derived from the current message statuses. See Results.
pause_reason Why a paused campaign was paused. See Statuses.
launched_at / completed_at When sending started and when the campaign finished; null until then.

Audience

{
  "include": [
    { "kind": "list", "id": "<LIST_ID>" },
    { "kind": "segment", "id": "<SEGMENT_ID>" }
  ],
  "exclude": [
    { "kind": "list", "id": "<LIST_ID>" }
  ]
}

audience.include needs at least one member; exclude is optional. Each member has a kind — list, segment, or upload (a recipient file created with POST /campaigns/uploads) — and an id. At launch the audience is resolved once and frozen:

  1. All include members are combined and all exclude members are removed.
  2. From lists and segments, only contacts in status subscribed remain; pending, unsubscribed, bounced, complained and erased contacts are left out.
  3. Every address on the suppression list is removed.

Recipients of an upload are not contacts: the file supplies the addresses and, through its column mapping, per-recipient variables. Uploaded addresses are de-duplicated within the upload and suppressed addresses are removed, but an address that appears both as a contact and in an upload is not de-duplicated between the two. POST /campaigns/{id}/preview returns the resolved recipient_count and up to 5 sample addresses before you launch.

Statuses

Status Meaning
draft Being configured. Fully editable with PATCH /campaigns/{id}, and the only status in which DELETE /campaigns/{id} works (it deletes permanently).
scheduled A blast waiting for schedule_at. Only throttle_per_hour can still be changed.
sending A blast is handing its recipients to sending, or an automation is running. Only throttle_per_hour can still be changed.
paused Stopped handing out new recipients, manually or automatically; messages already queued still go out. pause_reason says why: insufficient_credits (the balance ran out), credit_protection (the next batch would cross your protected minimum, see Billing & Credits), template_mismatch (the template was removed or is no longer marketing), sender_not_configured (no sender profile resolves for the campaign), or null for a manual pause.
sent A blast is finished: every recipient was handed to sending and every message reached a final status. Automations never reach sent.
canceled Stopped for good with POST /campaigns/{id}/cancel.

Lifecycle

draft ──schedule──► scheduled ──(schedule_at reached)──┐
  │                     │                              ▼
  └────────send─────────┴──────────send──────────► sending ──(all messages final)──► sent
                                                    │   ▲
                                              pause │   │ resume
                                                    ▼   │
                                                   paused
draft / scheduled / sending / paused ──cancel──► canceled
  • Schedule. POST /campaigns/{id}/schedule works only on a draft campaign; any other status is 409 invalid_state, so a scheduled campaign cannot be rescheduled. schedule_at must not be in the past — 422 schedule_in_past, with 120 seconds of tolerance for clock skew. The domain must be verified (domain_not_verified) and the template active (template_not_active).
  • Automatic launch. When schedule_at arrives the platform launches the campaign with the same checks as send. If the launch fails a check permanently, the campaign returns to draft so you can fix it.
  • Launch now. POST /campaigns/{id}/send launches a draft or scheduled campaign and answers 202 with the campaign in sending. Calling it again on a campaign that is already sending, sent or canceled returns its current state; a paused campaign must be resumed instead. For an automation, send is what starts it.
  • Pause and resume. POST /campaigns/{id}/pause moves sending to paused. POST /campaigns/{id}/resume moves paused back to sending and clears pause_reason; fix the cause first (top up credits, restore the template) or the campaign pauses again.
  • Completion. A blast moves from sending to sent once no recipients are left to hand out and every message has reached a final status. Messages held back by the warmup allowance keep a blast in sending until they are sent or fail.
  • Cancel. POST /campaigns/{id}/cancel works from any status except sent and canceled. Messages that were queued but not yet handed off become canceled and their credits are refunded; messages already handed off are not recalled. Canceling an automation also ends its active and waiting enrollments.

Launch checks

A launch (send, or the automatic launch of a scheduled blast) is refused when:

  • the campaign is not in a launchable status — 409 invalid_state;
  • your account is suspended or awaiting review — 409 tenant_suspended, 409 tenant_needs_moderation;
  • the content was rejected, is held for review, or changed after it was approved — 409 campaign_content_rejected, 409 campaign_needs_content_review, 409 template_changed_since_approval;
  • the domain is not verified or the template is not an active marketing template — domain_not_verified, template_not_active, template_class_mismatch;
  • a recipient’s variables fail the template’s variable rules — 422 invalid_variable;
  • no recipient would remain after exclusions — all_recipients_excluded;
  • the balance cannot fund the send — 402 with insufficient_credits or credit_protection;
  • recipients would receive more than one email that day and you did not choose how to handle it — 409 CONFLICT with a conflict summary (see below).

On a 409, branch on meta.details.code; a conflict summary has no code.

Same-day conflicts

POST /campaigns/{id}/conflicts reports recipients who would receive more than one email on the campaign’s target day (a calendar day in your account’s time zone), counting mail of any type already sent that day and other scheduled or sending campaigns. The summary gives target_date, audience_size, conflict_count, and the count by source. To launch anyway, pass conflict_resolution to schedule or send:

Value Effect
send_anyway Send to everyone as planned.
exclude_conflicts Skip the conflicting recipients. Excluding every recipient is refused.
delay_1_day Send to the conflicting recipients on the next day.
spread_schedule Spread the conflicting recipients over the following days.

Sending and credits

After launch, recipients are handed to sending in batches, paced by throttle_per_hour when set. Each batch reserves one credit per message before the messages are created; a batch that cannot be funded creates no messages and pauses the campaign with pause_reason: insufficient_credits or credit_protection. Resume it after topping up. See Billing & Credits.

Results

GET /campaigns/{id} returns stats, recalculated from the current status of the campaign’s messages shortly after each change, so the numbers can lag a few seconds behind events:

Counter Counts messages that are currently
sent sent or delivered — accepted by the provider and not bounced or complained.
delivered delivered. It can decrease when a delivered message later hard-bounces or is reported as spam.
bounced bounced.
complained complained.
failed failed or suppressed.
opened / clicked with at least one recorded open / click.

Queued, deferred and canceled messages are not counted. GET /campaigns/{id}/series?range=7d|30d|all returns the same counters per day, with totals for the period.

  • Campaigns tag — GET /campaigns, POST /campaigns, GET /campaigns/{id}, PATCH /campaigns/{id}, DELETE /campaigns/{id}, GET /campaigns/{id}/series, POST /campaigns/{id}/preview, POST /campaigns/{id}/conflicts, POST /campaigns/{id}/schedule, POST /campaigns/{id}/send, POST /campaigns/{id}/pause, POST /campaigns/{id}/resume, POST /campaigns/{id}/cancel, POST /campaigns/uploads.

See the API Reference for full schemas, and Send your first campaign for a worked example.