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

# Create campaign

`POST /campaigns`

Create a new email campaign. Campaigns are created as `DRAFT`; use `POST /campaigns/{id}/send` to send or schedule one.

The sender domain must already be verified for this project.

**Audience:** `audienceType` decides which companion field is required — `SEGMENT` requires `segmentId`, `FILTERED` requires `audienceCondition`, and `ALL` requires neither.

## Request body

- object
  - `name` string, required — Campaign name
  - `description` string — Campaign description
  - `subject` string, required — Email subject line
  - `body` string, required — HTML email body
  - `from` string, email, required — Sender email address (must be from verified domain)
  - `fromName` string — Sender name
  - `replyTo` string, email — Reply-to email address
  - `type` 'TRANSACTIONAL' | 'MARKETING' | 'HEADLESS' — Content type of the campaign email. Defaults to `MARKETING`.
  - `audienceType` 'ALL' | 'SEGMENT' | 'FILTERED', required — Target audience type
  - `segmentId` string, uuid — Segment ID. Required when `audienceType` is `SEGMENT`.
  - `audienceCondition` FilterCondition — A boolean tree over contact filters. `logic` combines the `groups`; the filters inside each group are always ANDed together.
    - `logic` 'AND' | 'OR', required
    - `groups` FilterGroup[], required
      - `filters` Filter[], required — Filters within a group are combined with AND.
        - `field` string, required — Contact field to test. Custom fields are addressed via the `data.` prefix (e.g. `data.plan`); standard fields are `email`, `subscribed`, `createdAt`. Event operators take an event name instead.
        - `operator` 'equals' | 'notEquals' | 'contains' | 'notContains' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual' | 'exists' | 'notExists' | 'within' | 'olderThan' | 'triggered' | 'triggeredWithin' | 'triggeredOlderThan' | 'notTriggered' | 'notTriggeredWithin' | 'memberOfSegment' | 'notMemberOfSegment', required
        - `value` unknown
        - `unit` 'days' | 'hours' | 'minutes' — Time unit for window operators (`within`, `olderThan`, `triggeredWithin`, `triggeredOlderThan`, `notTriggeredWithin`).
      - `conditions` FilterCondition — recursive

## Response `201`

Campaign created

- object
  - `success` boolean
  - `data` Campaign
    - `id` string
    - `name` string
    - `description` string, nullable
    - `subject` string
    - `body` string
    - `from` string, email
    - `fromName` string, nullable
    - `replyTo` string, email, nullable
    - `type` 'TRANSACTIONAL' | 'MARKETING' | 'HEADLESS' — Content type of the campaign email. Not to be confused with `audienceType`.
    - `status` 'DRAFT' | 'SCHEDULED' | 'SENDING' | 'SENT' | 'CANCELLED'
    - `audienceType` 'ALL' | 'SEGMENT' | 'FILTERED'
    - `audienceCondition` FilterCondition — A boolean tree over contact filters. `logic` combines the `groups`; the filters inside each group are always ANDed together.
      - `logic` 'AND' | 'OR', required
      - `groups` FilterGroup[], required
        - `filters` Filter[], required — Filters within a group are combined with AND.
          - `field` string, required — Contact field to test. Custom fields are addressed via the `data.` prefix (e.g. `data.plan`); standard fields are `email`, `subscribed`, `createdAt`. Event operators take an event name instead.
          - `operator` 'equals' | 'notEquals' | 'contains' | 'notContains' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual' | 'exists' | 'notExists' | 'within' | 'olderThan' | 'triggered' | 'triggeredWithin' | 'triggeredOlderThan' | 'notTriggered' | 'notTriggeredWithin' | 'memberOfSegment' | 'notMemberOfSegment', required
          - `value` unknown
          - `unit` 'days' | 'hours' | 'minutes' — Time unit for window operators (`within`, `olderThan`, `triggeredWithin`, `triggeredOlderThan`, `notTriggeredWithin`).
        - `conditions` FilterCondition — recursive
    - `segmentId` string, nullable — Set when `audienceType` is `SEGMENT`.
    - `scheduledFor` string, date-time, nullable — When the campaign is scheduled to send. Null for immediate or unsent campaigns.
    - `totalRecipients` integer
    - `sentCount` integer
    - `deliveredCount` integer
    - `openedCount` integer
    - `clickedCount` integer
    - `bouncedCount` integer
    - `projectId` string
    - `sentAt` string, date-time, nullable
    - `createdAt` string, date-time
    - `updatedAt` string, date-time

## Other responses

- `400` — `segmentId` missing for a `SEGMENT` campaign, or `audienceCondition` missing for a `FILTERED` campaign.
- `401` — Missing or invalid API key.
- `403` — The sender domain is not registered to this project, or has not completed DNS verification. Add and verify the domain in your project settings first.
- `422` — Request body failed schema validation. `error.errors` lists the offending fields.

---

[API](https://skmtc.dev/useplunk/apis/plunk-api.md) · [All operations](https://skmtc.dev/useplunk/apis/plunk-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/useplunk/plunk-api/revisions/a13aa5345f8e/schema)
