Campaigns
Create campaigns, choose their audiences, and send or schedule them.
Create a campaign
Creates a campaign in draft status. Requires an API key with the full scope. Emits a campaign.created event.
A new campaign has no recipients. Choose its audiences with Update a campaign, then send or schedule it. Credits are charged when the campaign sends: 2 credits per email.
/campaignsBody parameters
namestringrequiredsubjectstring{{first_name}}.from_emailstringnews@acme.com.from_namestringAcme. The message is sent from Acme <news@acme.com>.reply_tostringfrom_email when the campaign sends.htmlstring{{first_name}}, {{last_name}}, {{email}}, {{unsubscribe_url}} and {{cf.<key>}} for custom fields. Set the body when you create the campaign.textstringhtml.preview_textstringhtml if you need one.contentstringhtml and text, not content.content_typestringdefault: htmlcontent: html, text or mjml. Emailit doesn’t compile MJML; send the compiled HTML in html.Returns
Returns 201 Created with the campaign object. status is draft. The response doesn’t echo html, text or content.
Returns 400 if name is missing and 403 if the API key doesn’t have the full scope.
{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"status": "draft",
"subject": "October news for {{first_name}}",
"from_email": "news@acme.com",
"from_name": "Acme",
"reply_to": "support@acme.com",
"preview_text": null,
"content_type": "html",
"created_at": "2026-10-01T09:30:12.482193Z",
"updated_at": "2026-10-01T09:30:12.482193Z"
}{
"error": "Bad Request"
}{
"statusCode": 403,
"error": "Forbidden",
"message": "Permission denied: campaigns:create"
}Retrieve a campaign
Retrieves a campaign by its ID or name. Requires an API key with the full scope.
/campaigns/{id}Path parameters
idstringrequiredcmp_…) or the campaign name. URL-encode names that contain spaces or special characters.Returns
Returns the campaign object.
objectstringcampaign.idstringcmp_.statusstringdraft, scheduled, queued, sending, sent, canceled or archived. queued means a scheduled campaign has reached its send time and is waiting for a worker.namestringsubjectstringfrom_emailstring"" until set.from_namestring"" until set.reply_tostring"" means replies go to from_email.preview_textstring | nullcontent_typestringhtml, text or mjml for campaigns created through the API.scheduled_atstring | nullsent_atstring | nullrecipientsobject[]audience_id (aud_…) and exclude (true for an excluded audience).The body (html, text and content) isn’t included in the response. Engagement statistics are available in the dashboard under Email MarketingCampaigns.
Returns 404 if no campaign in the workspace matches id.
{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"status": "scheduled",
"subject": "October news for {{first_name}}",
"from_email": "news@acme.com",
"from_name": "Acme",
"reply_to": "support@acme.com",
"preview_text": null,
"content_type": "html",
"sent_at": null,
"scheduled_at": "2026-10-08 09:00:00+00",
"created_at": "2026-10-01 09:30:12.482193+00",
"updated_at": "2026-10-01 10:02:47.118204+00",
"recipients": [
{ "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV", "exclude": false },
{ "audience_id": "aud_3hRmYTSQ4ca6y9tbPI0IQPjVTL9", "exclude": true }
]
}{
"error": "Campaign not found"
}Update a campaign
Updates the fields you pass and leaves the others unchanged. Use it to choose the campaign’s audiences before you send it. Requires an API key with the full scope. Emits a campaign.updated event.
A scheduled campaign sends whatever is saved at its send time, so you can still edit it after scheduling.
/campaigns/{id}Path parameters
idstringrequiredcmp_…) or the campaign name.Body parameters
namestringsubjectstringfrom_emailstringfrom_namestringreply_tostringfrom_email.preview_textstringcontentstringcontent_typestringcontent: html, text or mjml.recipientsobject[]The audiences to target. Replaces the current list. Include at least one audience with exclude set to false.
audience_id(string, required): an audience ID (aud_…) in this workspace.exclude(boolean, defaultfalse):truesaves the audience as an exclusion. The dashboard subtracts excluded audiences from its recipient estimate, but the send itself doesn’t apply exclusions at the moment, so also remove those contacts from the included audiences.
Duplicate audience IDs are ignored. At send time, Emailit emails every subscribed contact of the included audiences once, and skips unsubscribed contacts and suppressed addresses.
The HTML and text body are set when you create the campaign. Unknown body fields are ignored.
Returns
Returns the updated campaign object, including recipients.
Returns 422 if recipients has no included audience or references an audience outside the workspace, and 404 if the campaign doesn’t exist.
{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"status": "draft",
"subject": "Your October update, {{first_name}}",
"from_email": "news@acme.com",
"from_name": "Acme",
"reply_to": "support@acme.com",
"preview_text": null,
"content_type": "html",
"scheduled_at": null,
"created_at": "2026-10-01 09:30:12.482193+00",
"updated_at": "2026-10-01 10:02:47.118204+00",
"recipients": [
{ "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV", "exclude": false },
{ "audience_id": "aud_3hRmYTSQ4ca6y9tbPI0IQPjVTL9", "exclude": true }
]
}{
"message": "Validation failed.",
"errors": {
"recipients": ["One or more audiences are invalid."]
}
}{
"error": "Campaign not found"
}List campaigns
Returns the workspace’s campaigns, newest first. Requires an API key with the full scope.
/campaignsQuery parameters
pageintegerdefault: 1limitintegerdefault: 10searchstringstatusstringdraft, scheduled, sending (also matches queued), sent, canceled, archived or all.matchstringall (default) requires every filter. or matches any filter. See Filtering.
orderstringSort key for this list. See the sort keys below.
directionstringasc or desc.
Filter keys
Filters use key.condition=value query parameters, for example status.exact=sent or created_at.after=2026-09-01. See Filtering for the conditions per type.
| Key | Type | Notes |
|---|---|---|
name |
string | |
subject |
string | |
status |
enum | draft, scheduled, queued, sending, sent, archived |
created_at |
date | |
sent_at |
date |
Sort keys for order: name, subject, status, created_at, sent_at.
Returns
Returns a page of campaign objects without reply_to, preview_text, content_type and recipients. Use Retrieve a campaign for those.
dataobject[]total_recordsintegernext_page_urlstring | nullnull on the last page. It carries only page and limit, so add your search and filters again when you follow it.previous_page_urlstring | nullnull on the first page.{
"data": [
{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"status": "sent",
"subject": "Your October update, {{first_name}}",
"from_email": "news@acme.com",
"from_name": "Acme",
"sent_at": "2026-10-08 09:00:04+00",
"scheduled_at": "2026-10-08 09:00:00+00",
"created_at": "2026-10-01 09:30:12.482193+00",
"updated_at": "2026-10-08 09:00:31+00"
}
],
"total_records": 34,
"next_page_url": "/v2/campaigns?page=2&limit=20",
"previous_page_url": null
}Send or schedule a campaign
Sends the campaign now, or schedules it when you pass a future scheduled_at. Requires an API key with the full scope and a verified workspace: unverified workspaces can’t send campaigns and get 403.
Before you send, make sure the campaign has a from_email on a verified domain, a subject, an html or text body, and at least one included audience (set with Update a campaign). Each email costs 2 credits.
/campaigns/{id}/sendPath parameters
idstringrequiredcmp_…) or the campaign name.Body parameters
scheduled_atstringWhen to send. Accepts ISO 8601 (2026-10-08T09:00:00Z), a Unix timestamp in seconds, or natural language such as tomorrow at 9am (interpreted in UTC). The time must be in the future and the campaign must be a draft.
Omit it to send now. To send a scheduled campaign early, call this endpoint without scheduled_at.
Returns
Send now: the status changes to sending and Emailit emits campaign.sending. Emailit then creates one email per recipient: every subscribed contact of the included audiences, deduplicated by address, without unsubscribed contacts and suppressed addresses. Once every recipient has been handed to the sending pipeline, the status changes to sent and Emailit emits campaign.sent. Track delivery in the dashboard or with email events.
Schedule: the status changes to scheduled and Emailit emits campaign.scheduled. At the scheduled time the campaign becomes queued (campaign.queued) and then sends as above.
The response contains object, id, name and the new status, plus scheduled_at when you schedule.
| Status | When |
|---|---|
403 |
The workspace isn’t verified, or the API key doesn’t have the full scope. |
404 |
No campaign matches id. |
422 |
scheduled_at can’t be parsed or isn’t in the future, or you tried to schedule a campaign that isn’t a draft. |
{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"status": "scheduled",
"scheduled_at": "2026-10-08T09:00:00.000000Z"
}{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"status": "sending"
}{
"code": "unverified_workspace_recipient",
"error": "Workspace not verified",
"message": "Unverified workspaces cannot send campaigns. You can send individual emails only to workspace members' account emails."
}{
"error": "Campaign cannot be scheduled",
"message": "Campaign status is 'sent'. Only draft campaigns can be scheduled."
}Cancel a campaign
Sets the campaign’s status to canceled and emits a campaign.canceled event. Requires an API key with the full scope.
Only campaigns in draft or sending status can be canceled; any other status returns 422. Canceling a campaign that is sending doesn’t recall emails that are already queued for delivery.
/campaigns/{id}/cancelPath parameters
idstringrequiredcmp_…) or the campaign name.Returns
Returns object, id, name and status (canceled).
Returns 422 if the campaign’s status isn’t draft or sending, and 404 if the campaign doesn’t exist.
{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"status": "canceled"
}{
"error": "Campaign cannot be canceled",
"message": "Campaign status is 'scheduled'. Only 'draft' or 'sending' campaigns can be canceled."
}{
"error": "Campaign not found"
}Delete a campaign
Permanently deletes a campaign. Requires an API key with the full scope. Emits a campaign.deleted event.
Deleting a campaign doesn’t affect emails that were already sent or queued. To stop a campaign that is sending, cancel it first.
/campaigns/{id}Path parameters
idstringrequiredcmp_…) or the campaign name.Returns
Returns object, id, name and deleted: true. Returns 404 if the campaign doesn’t exist.
{
"object": "campaign",
"id": "cmp_3zNrBaNjAoPK1l0IL2GQ3pDkubN",
"name": "October newsletter",
"deleted": true
}{
"error": "Campaign not found"
}