---
title: "Create a campaign"
method: POST
path: "/email/campaigns"
tags: ["Campaigns"]
---

# Create a campaign

`POST /email/campaigns`

## Request

- Base URL: `https://www.unitpost.com/api/v1`
- URL: `https://www.unitpost.com/api/v1/email/campaigns`
- Auth: HTTP bearer

Create a campaign in `draft`. Target either a saved segment (`segment_id`, see [Create a segment](#post-segments)) 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 published `template_id` (campaigns have no inline `html`/`text`). Nothing is sent yet — edit the draft with [Update a campaign](#patch-email-campaigns-id), check it with [Validate a campaign](#get-email-campaigns-id-validate), then launch it with [Send a campaign](#post-email-campaigns-id-send). Requires the `campaigns:write` capability.

## Request body

- CampaignCreate
  - `name` string, required
  - `template_id` string, required
  - `segment_id` string
  - `recipients` string[]
  - `topic_id` string
  - `from` string, required
  - `subject` string
  - `preview_text` string
  - `scheduled_at` string
  - `open_tracking` boolean, nullable
  - `click_tracking` boolean, nullable
  - `variable_fallbacks` object
  - `excluded_contact_ids` string[]

## Response `201`

Created.

- Campaign
  - `object` 'campaign', required
  - `id` string, required
  - `name` string, required
  - `template_id` string, required
  - `segment_id` string, required
  - `topic_id` string, nullable, required
  - `from` string, required
  - `subject` string, nullable, required
  - `preview_text` string, nullable, required
  - `open_tracking` boolean, nullable, required
  - `click_tracking` boolean, nullable, required
  - `status` 'draft' | 'scheduled' | 'sending' | 'paused' | 'sent' | 'canceled' | 'failed', required
  - `scheduled_at` string, nullable, required
  - `variable_fallbacks` object, required
  - `excluded_contact_ids` string[], required
  - `total_recipients` number, required
  - `sent_count` number, required
  - `failed_count` number, required
  - `pause_reason` object, nullable, required: Why the campaign is paused, or null when it isn't. `code` is `user` for a pause you made; `hard_bounce_rate` / `complaint_rate` when the per-campaign quality check paused it (with `metric`, `value`, `threshold`, `sample` and `window`); `quality_review` when the delivery reviewer did. `resume`: `self_serve` (resume after cleaning the list), `review_required` (a repeat pause; Resume answers 409 `review_required`), `account_review` (staff are reviewing the account), `resume_slowly` (a provider is limiting the sender).
    - `code` 'user' | 'hard_bounce_rate' | 'complaint_rate' | 'quality_review' | 'provider_limit' | 'failure_streak' | 'sender_unavailable' | 'template_unavailable' | 'billing' | 'not_drained' | 'sending_restricted' | 'workspace_suspended', required
    - `message` string, nullable, required
    - `metric` 'hard_bounce_rate' | 'complaint_rate', nullable, required
    - `value` number, nullable, required
    - `threshold` number, nullable, required
    - `sample` object, nullable, required
      - `events` number, required
      - `of` number, required
    - `window` object, nullable, required
      - `since` string, nullable, required
    - `decided_by` 'floor' | 'unit', nullable, required
    - `resume` 'self_serve' | 'review_required' | 'account_review' | 'resume_slowly', nullable, required
    - `paused_at` string, nullable, required
  - `created_at` string, required
  - `updated_at` string, required

## Other responses

- `401`: Missing or invalid API key.
- `422`: Validation error.

---

[API](https://skmtc.dev/unitpost/apis/unitpost-api.md) · [All operations](https://skmtc.dev/unitpost/apis/unitpost-api/llms.txt) · [OpenAPI document](https://skmtc.dev/unitpost/apis/unitpost-api/revisions/86ff7c890967?raw)
