v1 · API reference

Campaigns

One-to-many sends to a segment or an inline recipient list. Draft, validate, schedule, send, pause, resume, and cancel — with a pre-send report that catches problems before anything goes out.

Base URL: https://www.unitpost.com/api/v1/api/v1

Campaigns

List campaigns

GET/api/v1/api/v1/campaigns

GET /campaigns
curl -X GET 'https://www.unitpost.com/api/v1/api/v1/campaigns' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response · 200
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "campaign",
      "id": "id_123",
      "name": "Example",
      "template_id": "template_123",
      "segment_id": "segment_123",
      "topic_id": "topic_123",
      "from": "you@yourdomain.com",
      "subject": "Welcome to Acme",
      "preview_text": "string",
      "open_tracking": false,
      "click_tracking": false,
      "status": "draft",
      "scheduled_at": "2026-01-01T00:00:00.000Z",
      "variable_fallbacks": {},
      "excluded_contact_ids": [
        "string"
      ],
      "total_recipients": 1,
      "sent_count": 1,
      "failed_count": 1,
      "created_at": "2026-01-01T00:00:00.000Z",
      "updated_at": "2026-01-01T00:00:00.000Z"
    }
  ]
}

Retrieve a list of your campaigns, cursor-paginated. Each row is a reference to one campaign and its current status (draft, scheduled, sending, sent, …). Use a campaign's id with Retrieve a campaign for the full record, or with List sent emails (?campaign_id=) to see the individual emails it produced. Requires the campaigns:read capability.

Parameters3 fields
FieldTypeDescription
limitinteger · queryMax rows to return (default 20, max 100).
afterstring · queryReturn rows after this id (forward paging).
beforestring · queryReturn rows before this id (backward paging).
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401422

Create a campaign

POST/api/v1/api/v1/campaigns

POST /campaigns
curl -X POST 'https://www.unitpost.com/api/v1/api/v1/campaigns' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "June newsletter",
    "subject": "What's new",
    "from": "you@yourdomain.com"
  }'
Response · 201
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Create a campaign in draft. Target either a saved segment (segment_id, see Create a segment) or an inline recipients list, and optionally scope it to a subscription topic (topic_id) so contacts who opted out of that topic are skipped automatically. Content comes from a template_id or inline html/text. Nothing is sent yet — edit the draft with Update a campaign, check it with Validate a campaign, then launch it with Send a campaign. Requires the campaigns:write capability.

Request body13 fields
FieldTypeDescription
namerequiredstring
template_idrequiredstring
segment_idstring
recipientsstring[]
topic_idstring
fromrequiredstring
subjectstring
preview_textstring
scheduled_atstring
open_trackingobject
click_trackingobject
variable_fallbacksobject
excluded_contact_idsstring[]
Response · 201 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
201401422

Retrieve a campaign

GET/api/v1/api/v1/campaigns/{id}

GET /campaigns/{id}
curl -X GET 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response · 200
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Retrieve a single campaign by id, including its status, targeting, content references, and schedule. To see the individual emails a sent campaign produced, filter List sent emails by campaign_id; for a pre-send readiness report, use Validate a campaign. Requires the campaigns:read capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401404

Update a campaign

PATCH/api/v1/api/v1/campaigns/{id}

PATCH /campaigns/{id}
curl -X PATCH 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ }'
Response · 200
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Edit a campaign while it's still in draft (content edits after that return 409). Beyond content and targeting, you can: send archived to archive/restore it; set variable_fallbacks (fill-only per-variable defaults) and/or excluded_contact_ids to resolve a send blocked by missing variables — the API equivalent of the dashboard's pre-send fix; set topic_id to scope it to a subscription topic (or null to clear); and set open_tracking/click_tracking (tri-state; null inherits the template/domain default). See what's blocking a send with Validate a campaign. Requires the campaigns:write capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Request body6 fields
FieldTypeDescription
archivedboolean
topic_idobject
open_trackingobject
click_trackingobject
variable_fallbacksobject
excluded_contact_idsstring[]
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401404409422

Delete a campaign

DELETE/api/v1/api/v1/campaigns/{id}

DELETE /campaigns/{id}
curl -X DELETE 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Permanently delete a campaign that's in draft or a terminal state (sent, canceled, failed). A campaign that's mid-flight can't be deleted — stop it first with Cancel a campaign. Requires the campaigns:write capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
204401404409

Send a campaign

POST/api/v1/api/v1/campaigns/{id}/send

POST /campaigns/{id}/send
curl -X POST 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123/send' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response · 200
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Validate a campaign and then send it — immediately, or at its scheduled_at if one is set. If anything blocks the send (unverified domain, missing template variables, empty audience, …) you get a 409 with each blocking reason itemized in error.details[]. To see the full pre-send report without attempting a send, use Validate a campaign; fix variable issues via Update a campaign. A launched campaign can be paused or canceled. Requires the campaigns:send capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401404409

Cancel a campaign

POST/api/v1/api/v1/campaigns/{id}/cancel

POST /campaigns/{id}/cancel
curl -X POST 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123/cancel' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response · 200
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Cancel a draft, scheduled, or sending campaign. Cancelation is terminal — the campaign can't be restarted afterwards. Messages already sent to recipients are not recalled. If you only want to stop temporarily, use Pause a campaign instead. Requires the campaigns:send capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401404409

Pause a campaign

POST/api/v1/api/v1/campaigns/{id}/pause

POST /campaigns/{id}/pause
curl -X POST 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123/pause' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response · 200
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Pause a scheduled or sending campaign. A paused send stops cleanly between recipients — already-sent messages are untouched, and no partial email is ever produced. Continue where it left off with Resume a campaign, or stop it for good with Cancel a campaign. Requires the campaigns:send capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401404409

Resume a campaign

POST/api/v1/api/v1/campaigns/{id}/resume

POST /campaigns/{id}/resume
curl -X POST 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123/resume' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response · 200
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Resume a paused campaign. Sending continues with the recipients that hadn't been reached yet; anyone already delivered to is skipped idempotently, so nobody receives the campaign twice. Pause a send with Pause a campaign. Requires the campaigns:send capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401404409

Reschedule a campaign

POST/api/v1/api/v1/campaigns/{id}/reschedule

POST /campaigns/{id}/reschedule
curl -X POST 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123/reschedule' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "scheduled_at": "2026-07-01T15:00:00Z"
  }'
Response · 200
{
  "object": "campaign",
  "id": "id_123",
  "name": "Example",
  "template_id": "template_123",
  "segment_id": "segment_123",
  "topic_id": "topic_123",
  "from": "you@yourdomain.com",
  "subject": "Welcome to Acme",
  "preview_text": "string",
  "open_tracking": false,
  "click_tracking": false,
  "status": "draft",
  "scheduled_at": "2026-01-01T00:00:00.000Z",
  "variable_fallbacks": {},
  "excluded_contact_ids": [
    "string"
  ],
  "total_recipients": 1,
  "sent_count": 1,
  "failed_count": 1,
  "created_at": "2026-01-01T00:00:00.000Z",
  "updated_at": "2026-01-01T00:00:00.000Z"
}

Move a scheduled campaign to a new send time. Only campaigns that haven't started sending can be rescheduled — once a send is in flight, pause or cancel it instead. Requires the campaigns:send capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Request body1 field
FieldTypeDescription
scheduled_atrequiredstringNew send time, ISO 8601. Only SCHEDULED campaigns can be moved.
Response · 200 fields20 fields
FieldTypeDescription
objectrequiredstring
idrequiredstring
namerequiredstring
template_idrequiredstring
segment_idrequiredstring
topic_idrequiredobject
fromrequiredstring
subjectrequiredobject
preview_textrequiredobject
open_trackingrequiredobject
click_trackingrequiredobject
statusrequiredenumdraft | scheduled | sending | paused | sent | canceled | failed
scheduled_atrequiredobject
variable_fallbacksrequiredobject
excluded_contact_idsrequiredstring[]
total_recipientsrequirednumber
sent_countrequirednumber
failed_countrequirednumber
created_atrequiredstring
updated_atrequiredstring
200401404409422

Validate a campaign

GET/api/v1/api/v1/campaigns/{id}/validate

GET /campaigns/{id}/validate
curl -X GET 'https://www.unitpost.com/api/v1/api/v1/campaigns/cmp_123/validate' \
  -H 'Authorization: Bearer YOUR_API_KEY'
Response · 200
{
  "object": "campaign_validation",
  "total_members": 1,
  "sendable": 0,
  "suppressed": 0,
  "excluded": 0,
  "missing_data": [
    {
      "contact_id": "contact_123",
      "email": "customer@example.com",
      "missing": [
        "string"
      ]
    }
  ],
  "unresolved_variables": [
    "string"
  ],
  "can_send": false
}

Run a non-mutating pre-send check and get the full readiness report: how many contacts will receive the campaign, how many are suppressed or excluded, which contacts are missing template variables, and a final can_send verdict. Nothing is sent or changed. Fix reported variable gaps with Update a campaign (variable_fallbacks / excluded_contact_ids), then launch with Send a campaign. Requires the campaigns:read capability.

Parameters1 field
FieldTypeDescription
idrequiredstring · path
Response · 200 fields8 fields
FieldTypeDescription
objectrequiredenumcampaign_validation
total_membersrequiredinteger
sendablerequiredinteger
suppressedrequiredinteger
excludedrequiredinteger
missing_datarequiredobject[]
unresolved_variablesrequiredstring[]
can_sendrequiredboolean
200401404