Campaigns
A campaign is an email you send to a selection of contacts in your account.
Overview
Each campaign has a name, a status that tracks where it is in its lifecycle, and
the details of the message to send — its subject, preview text, sender, and
audience. A campaign starts as a draft and is built up incrementally across
update calls.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /campaigns | List campaigns |
| POST | /campaigns | Create a campaign |
| GET | /campaigns/{uuid} | Get a campaign |
| PATCH | /campaigns/{uuid} | Update a campaign |
| DELETE | /campaigns/{uuid} | Delete a campaign |
| POST | /campaigns/{uuid}/send | Send a campaign now |
| POST | /campaigns/{uuid}/test | Send a test email |
| POST | /campaigns/{uuid}/schedule | Schedule a campaign |
| POST | /campaigns/{uuid}/unschedule | Unschedule a campaign |
| POST | /campaigns/{uuid}/cancel | Cancel a campaign |
| GET | /campaigns/{uuid}/contacts | List a campaign's live audience |
| GET | /campaigns/{uuid}/recipients | List who a campaign was sent to |
| GET | /campaigns/{uuid}/link-clicks | List a campaign's link clicks |
Statuses
A campaign's status tracks where it is in its lifecycle:
| Status | Meaning |
|---|---|
draft | Being edited. Not scheduled or sent. |
scheduled | Scheduled to send at a future time. |
queued | Queued for sending. |
sending | Currently being sent. |
sent | Fully sent. |
failed | Sending did not complete successfully. |
canceled | Canceled before it finished sending. |
Editing and deleting by status
Which write operations a campaign allows depends on its status:
- A campaign can be updated only while its status is
draft,scheduled, orfailed. Updating a campaign in any other status returns409 CAMPAIGN_NOT_EDITABLE. - A campaign can be deleted only while its status is
draft,sent,failed, orcanceled. Deleting a campaign in any other status returns409 CAMPAIGN_NOT_DELETABLE.
Sending lifecycle
Which lifecycle transition a campaign allows also depends on its status:
- A campaign can be sent (Send Campaign) or scheduled
(Schedule Campaign) only while its status is
draft,failed, orscheduled, and only once it is ready (see Readiness). Any other status returns409 CAMPAIGN_NOT_SENDABLE, and a campaign that is not ready returns422 CAMPAIGN_NOT_READY. - A
scheduledcampaign can be unscheduled (Unschedule Campaign), returning it todraft. Any other status returns409 CAMPAIGN_NOT_UNSCHEDULABLE. - A
queuedorsendingcampaign can be canceled (Cancel Campaign). Any other status returns409 CAMPAIGN_NOT_CANCELABLE. Ascheduledcampaign is unscheduled, not canceled.
While one of these transitions is being processed for a campaign, a concurrent
transition on the same campaign returns 409 CAMPAIGN_TRANSITION_IN_PROGRESS;
retry after a moment.
To see who a campaign would send to right now, list its live audience with List Campaign Contacts. To see who a sent campaign was actually sent to and how each recipient engaged, use List Campaign Recipients.
Sending a test
Send a Test Email sends a one-off preview to a single address
without changing the campaign's status, reaching its audience, or recording
recipients, so it works from any status. It needs only a subject, a body, and a
sender identity.
Audience
A campaign's audience is a contact list, a contact segment, or both. You set it
with the list_uuid and segment_uuid fields on Create
Campaign and Update Campaign, and read
it back in the audience object:
| Field | Type | Description |
|---|---|---|
list_uuid | string|null | UUID of the contact list the campaign sends to. null when not set. |
segment_uuid | string|null | UUID of the contact segment the campaign sends to. null when not set. |
A list_uuid or segment_uuid that does not belong to your account is rejected
with 422 VALIDATION_ERROR.
Readiness
Every single-campaign response carries a readiness object describing whether
the campaign is ready to send:
| Field | Type | Description |
|---|---|---|
can_send | boolean | true only when every requirement is met (missing is empty). |
missing | array | The requirements not yet met. |
Each entry in missing names a requirement and how to satisfy it:
| Requirement | How to satisfy |
|---|---|
from_name | Set from_name on the campaign. |
sender_email | Set from_identity to an enabled sender identity. |
subject | Set subject on the campaign. |
content | Set the email body with the content field (Markdown) on Create Campaign or Update Campaign, or build it in the dashboard. See Campaign body. |
audience | Set list_uuid and/or segment_uuid. |
A campaign with no body lists content in missing, and can_send reports
false until the body is set. Once a body is set, content drops out of
missing.
Campaign object
Create, Get, and Update return the full campaign object:
| Field | Type | Description |
|---|---|---|
uuid | string | Unique identifier for the campaign |
name | string | Campaign name |
status | string | Current status (see Statuses) |
subject | string|null | Email subject line. null until set. |
preheader | string|null | Preview text shown after the subject in the inbox. null until set. |
from_name | string|null | Sender display name. null until set. |
reply_to | string|null | Reply-to email address. null until set. |
from_identity | string|null | UUID of the sending identity the campaign sends from. null until set. |
from_email | string|null | Email address of the selected sending identity. null when no identity is set. |
content | string|null | The email body as Markdown, or null — either no body is set, or the body uses layout with no Markdown form (distinguish via content_editable_via_api). See Campaign body. |
content_editable_via_api | boolean | false when the body uses layout the API can't author (see Campaign body). |
content_overwrite_required | boolean | true when replacing the body with content requires overwrite: true (see Campaign body). |
audience | object | The campaign's recipients (see Audience). |
readiness | object | Whether the campaign is ready to send (see Readiness). |
created_at | string | ISO 8601 timestamp (UTC) |
updated_at | string | ISO 8601 timestamp (UTC) |
last_updated_at | string|null | ISO 8601 timestamp (UTC) the campaign was last edited. |
The List Campaigns endpoint returns a condensed version of
each campaign: it omits preheader, reply_to, from_identity, from_email,
content, content_editable_via_api, content_overwrite_required,
audience, readiness, and last_updated_at, and instead includes language,
scheduled_at, and recipients_count.
Campaign body
The campaign body is the email's content. Set it with the content field on
Create Campaign and Update Campaign, read
it back in the content field, and send content: null to clear it. The body can
also be edited in the dashboard.
Writing the body
content is a Markdown string, up to 65000 characters. Supported:
- Headings (levels 1–6)
- Bold and italic text
- Links
- Bullet and numbered lists
- Blockquotes
- Fenced and indented code blocks
- Images
- Horizontal rules (
---) - Hard line breaks
Tables, strikethrough, task lists, inline `code`, and raw HTML are not
supported — they are silently dropped or flattened to plain text, so stick to the
supported subset.
Reading the body back
content returns the stored body as Markdown, or null when no body is set or the
body uses layout that has no Markdown form. Two flags describe whether that body
can be rewritten through the API:
| Field | Type | Description |
|---|---|---|
content_editable_via_api | boolean | false when the body uses layout the API can't author (multi-column layouts, social-icon rows, countdown timers); the returned content is then a lossy approximation. |
content_overwrite_required | boolean | true when the existing body carries formatting Markdown can't preserve — the layout above, or styling such as custom colors, fonts, buttons, or styled images. |
When both flags are false, the body is plain enough to overwrite freely.
Overwriting a richer body
To replace a body that reports content_overwrite_required: true, set
overwrite: true on your Update Campaign call — this
permanently discards the existing styling or layout. Without it, the write is
rejected with 409 CONTENT_OVERWRITE_REQUIRED. A body that is
content_editable_via_api: false can only be changed by editing it in the
dashboard, or by passing overwrite: true to replace it with Markdown.