# Create a campaign

`POST /v1/campaigns`

Requires an API key with the `campaigns:write` scope. [About keys and scopes](/docs/api/authentication)

Creates a campaign in `draft`, targeting email, push, or both. A draft saves unfinished: only `name` is required, and the audience and each channel's content may come later (content for a channel not selected is refused). What is given is checked, so a bad merge tag is caught on save. Sending, scheduling or confirming needs it whole: 409 `DRAFT_INCOMPLETE` lists each missing field in `details.issues`, and the eligibility check shows the same. Every contact receives each selected channel they can be reached on and have consented to. Email content is a template snapshot — a schema version, block tree and theme — not a subject and body; build it from a template rather than by hand. Creating does not send: use the send or schedule operation once the campaign is ready.

## Request body

- `name` (string, required, 1–255 characters)
- `channels` (string[], optional, at least 1 item) — The channels to send on; each needs its own content. Defaults to `email`.
- `groupId` (string, optional, uuid) — Send to this group. Choose at most one audience.
- `segmentId` (string, optional, uuid) — Send to this segment. Choose at most one audience.
- `audienceRules` (object, optional) — Send to contacts matching these rules. Choose at most one audience.
  - One of: **Match all**
    - `all` (object[], required, at least 1 item, at most 20 items) — Every rule or nested group must match.
  - One of: **Match any**
    - `any` (object[], required, at least 1 item, at most 20 items) — At least one rule or nested group must match.
- `topicId` (string | null, optional, uuid) — Send only to contacts the topic reaches. Omit to reach everyone with consent.
- `messageLimits` (MessageLimitUseInput, optional)
  - `apply` (boolean, required)
  - `retryHours` (integer, optional, min 0, max 48)
- `utm` (object | null, optional) — UTM parameters added to the links in the email.
  - `enabled` (boolean, required)
  - `source` (string, optional, 1–100 characters)
  - `medium` (string, optional, 1–100 characters)
  - `campaign` (string, optional, 1–100 characters)
  - `content` (string, optional, 1–100 characters)
  - `term` (string, optional, 1–100 characters)
- `content` (object, optional, default `{}`) — The content for each selected channel. A draft may leave parts out.
  - `email` (object, optional) — Subject, preview text and sender. Needed to send once `channels` has `email`.
    - `subject` (string, optional, 1–998 characters) — The subject line. Merge tags are allowed.
    - `previewText` (string, optional, 1–255 characters) — Shown by inboxes after the subject. Merge tags are allowed; keep it near 100 characters.
    - `from` (string, optional, 1–320 characters) — The sender address, on a verified sending domain.
    - `replyTo` (string, optional, email) — Where replies go.
  - `templateSnapshot` (TemplateSnapshotInput, optional) — The email design. Needed to send once `channels` has `email`.
    - `schemaVersion` (integer, required, min > 0, max 1000) — The block format version; use the current one, 1.
    - `blocks` (EmailBlockInput[], required, at least 1 item, at most 200 items) — At most 200 blocks across the entire tree and five levels including the root. Footer blocks must remain at root. Known child fields are validated recursively.
      - `id` (string, required, 1–128 characters) — Identifies the block within the template; unique among its blocks.
      - `type` (string, required, 1–64 characters) — The block type: `header`, `heading`, `text`, `image`, `button`, `divider`, `spacer`, `columns`, `columns3`, `imagetext`, `imagegroup`, `boxed`, `quote`, `list`, `product`, `video`, `social`, `menu`, `html`, `section`, `footer`, `repeat`, `shared`. Each type reads its own fields next to `type`; an unknown type is kept but not validated.
      - `style` (EmailBlockStyleInput, optional) — Visual overrides for this block, such as colours, font and spacing.
        - `backgroundColor` (string, optional) — Fill behind the block.
        - `fontFamily` (string, optional, one of `Arial`, `Verdana`, `Georgia`, `Tahoma`, `Times New Roman`, `Courier New`, `Inter`, `Roboto`, `Open Sans`, `Lato`, `Montserrat`, `Poppins`, `DM Sans`, `Nunito`, `Work Sans`, `Oswald`, `Playfair Display`, `Merriweather`, `Lora`, `Libre Baskerville`, `EB Garamond`) — A font every client has (Arial, Verdana, Georgia, Tahoma, Times New Roman, Courier New), or a web font, which shows where the client loads it and falls back to a safe font elsewhere.
        - `fontSize` (integer, optional, min 12, max 72) — Text size in px, 12 to 72.
        - `mobileFontSize` (integer, optional, min 12, max 72) — Text size in px on phones (screens up to 620px), 12 to 72.
        - `padding` (object, optional) — Space inside the block in px, 0 to 100 per side; omitted sides keep their default.
        - `borderWidth` (integer, optional, min 0, max 10) — Border thickness in px; 0 for none.
        - `borderStyle` (string, optional, one of `solid`, `dashed`, `dotted`) — Border line style.
        - `borderColor` (string, optional) — Border colour.
        - `borderRadius` (integer, optional, min 0, max 100) — Corner rounding in px, 0 to 100.
        - `textColor` (string, optional) — Text colour.
        - `linkColor` (string, optional) — Colour of links in the text.
        - `letterSpacing` (number, optional, min -2, max 20) — Space between letters in px, such as 3 for a spaced-out eyebrow line.
        - `textTransform` (string, optional, one of `none`, `uppercase`) — Set to `uppercase` to capitalise all text.
        - `lineHeight` (number, optional, min 1, max 3) — A multiple of the font size.
        - `fontWeight` (string, optional, one of `regular`, `bold`) — Text weight.
        - `fontStyle` (string, optional, one of `normal`, `italic`) — Text style.
        - `inset` (boolean, optional) — Draw the background and border as a box within the content width, not across the whole row.
      - `visibleIf` (object, optional) — Show the block only to contacts these segment rules match, checked as each message is rendered. Omit to show it to everyone. Not allowed on the footer.
        - One of: **Match all**
        - One of: **Match any**
      - `showOn` (string, optional, one of `mobile`, `desktop`) — Show the block only on phones (screens up to 620px) or only on wider screens. Clients that ignore screen size show the desktop version, and so does plain text. Omit to show it everywhere. Not allowed on the footer.
      - `columns` (object[][], optional, at most 3 items) — Nested child block arrays: exactly two for columns, three for columns3. Each child follows this block contract. Omit to retain legacy text columns.
      - `blocks` (object[], optional, at most 200 items) — A section's child blocks, drawn inside its box; or a repeat block's, written out once per item of its `list`, where `{{ item.<field> }}` reads that item. Repeats do not nest.
    - `theme` (EmailThemeInput, required) — Theme values this email overrides.
      - `font` (string, optional, up to 200 characters) — Body font stack, such as `Inter, sans-serif`.
      - `hfont` (string, optional, up to 200 characters) — Heading font stack.
      - `bg` (string, optional) — Background of the email card, as hex.
      - `canvas` (string, optional) — Background behind the card, as hex.
      - `text` (string, optional) — Default text colour, as hex.
      - `accent` (string, optional) — Brand colour for buttons and highlights, as hex.
      - `link` (string, optional) — Link colour, as hex.
      - `width` (string, optional, one of `560px`, `600px`, `680px`) — Width of the email card on wide screens.
      - `radius` (string, optional, one of `0px`, `6px`, `12px`, `16px`, `24px`, `999px`) — Corner rounding of the email's card.
      - `btnRadius` (string, optional, one of `0px`, `6px`, `12px`, `16px`, `24px`, `999px`) — Button corners; `radius` rounds the email's card and, when this is unset, buttons.
      - `btn` (string, optional, one of `solid`, `outline`) — Button style.
    - `plainTextOverride` (string | null, optional, up to 100000 characters) — Plain text sent instead of the version generated from the blocks.
  - `pushProjectId` (string, optional, uuid) — The push app to send through. Needed to send once `channels` has `push`.
  - `push` (object, optional) — The notification content. Needed to send once `channels` has `push`.
    - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
    - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
    - `actionUrl` (string, optional, uri, up to 2048 characters) — The HTTPS page opened when the notification is tapped.
    - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
    - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
    - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
    - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
      - `title` (string, required, 1–30 characters) — The button label.
      - `url` (string, required, uri, up to 2048 characters) — The HTTPS page the button opens.
    - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
    - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
    - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
    - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
    - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.
    - `overrides` (object, optional) — Fields that differ on one platform, each replacing the shared one there (a whole `data` map, not merged key by key). On iOS and Android `actionUrl` may be an app link such as `myapp://orders`; the shared one and the web one must be HTTPS.
      - `ios` (object, optional) — Replaces shared fields on iOS.
        - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
        - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
        - `actionUrl` (string, optional, up to 2048 characters)
        - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
        - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
        - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
        - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
        - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
        - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
        - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
        - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
        - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.
        - `interruptionLevel` (string, optional, one of `passive`, `active`, `time-sensitive`) — How iOS presents it: `passive` arrives silently, `time-sensitive` breaks through Focus and needs the app's Time Sensitive Notifications capability.
        - `threadId` (string, optional) — Notifications with the same thread are grouped together in Notification Center.
      - `android` (object, optional) — Replaces shared fields on Android.
        - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
        - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
        - `actionUrl` (string, optional, up to 2048 characters)
        - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
        - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
        - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
        - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
        - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
        - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
        - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
        - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
        - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.
        - `channelId` (string, optional) — The Android notification channel to post in, which the app creates; it sets the sound and importance a person can tune, e.g. one for offers and one for orders.
      - `web` (object, optional) — Replaces shared fields on web.
        - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
        - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
        - `actionUrl` (string, optional, uri, up to 2048 characters) — The HTTPS page opened when the notification is tapped.
        - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
        - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
        - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
        - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
        - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
        - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
        - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
        - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
        - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.

## Returns

Returns `201 Created` with a `Campaign` in `data`. Otherwise returns an error; [Errors](/docs/api/errors) lists every code and which to retry.

- `id` (string, required, uuid)
- `name` (string, required)
- `channels` (string[], required) — The channels it sends on.
- `groupId` (string | null, required, uuid) — The group it goes to, if any.
- `segmentId` (string | null, required, uuid) — The segment it goes to, if any.
- `audienceRules` (object | null, required) — Inline rules choosing the audience, if any. At most one audience is set.
  - One of: **Match all**
    - `all` (object[], required, at least 1 item, at most 20 items) — Every rule or nested group must match.
  - One of: **Match any**
    - `any` (object[], required, at least 1 item, at most 20 items) — At least one rule or nested group must match.
- `topicId` (string | null, required, uuid) — Sends only to contacts the topic reaches; `null` sends to everyone with consent.
- `messageLimits` (MessageLimitUse, required)
  - `apply` (boolean, required)
  - `retryHours` (integer, optional, min 0, max 48)
- `utm` (object | null, required) — UTM parameters added to the links in the email.
  - `enabled` (boolean, required)
  - `source` (string, optional, 1–100 characters)
  - `medium` (string, optional, 1–100 characters)
  - `campaign` (string, optional, 1–100 characters)
  - `content` (string, optional, 1–100 characters)
  - `term` (string, optional, 1–100 characters)
- `content` (CampaignContent, required)
  - `email` (object, optional) — The email fields; any may be missing on a draft.
    - `subject` (string, optional, 1–998 characters) — The subject line. Merge tags are allowed.
    - `previewText` (string, optional, 1–255 characters) — Shown by inboxes after the subject. Merge tags are allowed; keep it near 100 characters.
    - `html` (string, optional, 1–512000 characters) — The HTML body. Rendered from the design when one is set.
    - `text` (string, optional, 1–512000 characters) — The plain-text alternative.
    - `from` (string, optional, 1–320 characters) — The sender address, on a verified sending domain.
    - `replyTo` (string, optional, email) — Where replies go.
  - `pushProjectId` (string, optional, uuid) — The push app the notification goes out through.
  - `push` (object, optional) — The notification content.
    - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
    - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
    - `actionUrl` (string, optional, uri, up to 2048 characters) — The HTTPS page opened when the notification is tapped.
    - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
    - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
    - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
    - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
      - `title` (string, required, 1–30 characters) — The button label.
      - `url` (string, required, uri, up to 2048 characters) — The HTTPS page the button opens.
    - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
    - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
    - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
    - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
    - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.
    - `overrides` (object, optional) — Fields that differ on one platform, each replacing the shared one there (a whole `data` map, not merged key by key). On iOS and Android `actionUrl` may be an app link such as `myapp://orders`; the shared one and the web one must be HTTPS.
      - `ios` (object, optional) — Replaces shared fields on iOS.
        - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
        - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
        - `actionUrl` (string, optional, up to 2048 characters)
        - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
        - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
        - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
        - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
        - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
        - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
        - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
        - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
        - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.
        - `interruptionLevel` (string, optional, one of `passive`, `active`, `time-sensitive`) — How iOS presents it: `passive` arrives silently, `time-sensitive` breaks through Focus and needs the app's Time Sensitive Notifications capability.
        - `threadId` (string, optional) — Notifications with the same thread are grouped together in Notification Center.
      - `android` (object, optional) — Replaces shared fields on Android.
        - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
        - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
        - `actionUrl` (string, optional, up to 2048 characters)
        - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
        - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
        - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
        - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
        - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
        - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
        - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
        - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
        - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.
        - `channelId` (string, optional) — The Android notification channel to post in, which the app creates; it sets the sound and importance a person can tune, e.g. one for offers and one for orders.
      - `web` (object, optional) — Replaces shared fields on web.
        - `title` (string, optional, 1–100 characters) — The notification title (up to 100 characters).
        - `body` (string, optional, 1–500 characters) — The notification text (up to 500 characters).
        - `actionUrl` (string, optional, uri, up to 2048 characters) — The HTTPS page opened when the notification is tapped.
        - `imageUrl` (string, optional, uri, up to 2048 characters) — A large picture: web and Android show it; iOS needs the app's service extension.
        - `iconUrl` (string, optional, uri, up to 2048 characters) — Web only; iOS and Android show the app's own notification icon.
        - `data` (object, optional) — Up to 10 string key-value pairs passed to the app untouched, for deep links and in-app routing.
        - `buttons` (object[], optional, at least 1 item, at most 2 items) — Up to 2 action buttons.
        - `collapseKey` (string, optional) — A newer notification with the same key replaces the older one on the device.
        - `sound` (string, optional, 1–64 characters) — The name of a sound the app ships; the device's default if unset.
        - `badge` (integer, optional, min 0, max 99999) — The number shown on the iOS app icon.
        - `ttlSeconds` (integer, optional, min 0, max 2419200) — Seconds a provider keeps trying an offline device (up to 28 days); defaults to 86400 (one day).
        - `urgency` (string, optional, one of `high`, `normal`) — `high` wakes the device now; `normal` lets it wait for a good moment to save battery. Unset, a transactional push is `high` and any other `normal`.
  - `templateSnapshot` (object, optional) — The email design, copied into the campaign so later template edits do not change it.
    - `schemaVersion` (integer, required, min > 0, max 1000)
    - `blocks` (EmailBlock[], required, at least 1 item, at most 200 items) — At most 200 blocks across the entire tree and five levels including the root. Footer blocks must remain at root. Known child fields are validated recursively.
      - `id` (string, required, 1–128 characters) — Identifies the block within the template; unique among its blocks.
      - `type` (string, required, 1–64 characters) — The block type: `header`, `heading`, `text`, `image`, `button`, `divider`, `spacer`, `columns`, `columns3`, `imagetext`, `imagegroup`, `boxed`, `quote`, `list`, `product`, `video`, `social`, `menu`, `html`, `section`, `footer`, `repeat`, `shared`. Each type reads its own fields next to `type`; an unknown type is kept but not validated.
      - `style` (EmailBlockStyle, optional) — Visual overrides for this block, such as colours, font and spacing.
        - `backgroundColor` (string, optional) — Fill behind the block.
        - `fontFamily` (string, optional, one of `Arial`, `Verdana`, `Georgia`, `Tahoma`, `Times New Roman`, `Courier New`, `Inter`, `Roboto`, `Open Sans`, `Lato`, `Montserrat`, `Poppins`, `DM Sans`, `Nunito`, `Work Sans`, `Oswald`, `Playfair Display`, `Merriweather`, `Lora`, `Libre Baskerville`, `EB Garamond`) — A font every client has (Arial, Verdana, Georgia, Tahoma, Times New Roman, Courier New), or a web font, which shows where the client loads it and falls back to a safe font elsewhere.
        - `fontSize` (integer, optional, min 12, max 72) — Text size in px, 12 to 72.
        - `mobileFontSize` (integer, optional, min 12, max 72) — Text size in px on phones (screens up to 620px), 12 to 72.
        - `padding` (object, optional) — Space inside the block in px, 0 to 100 per side; omitted sides keep their default.
        - `borderWidth` (integer, optional, min 0, max 10) — Border thickness in px; 0 for none.
        - `borderStyle` (string, optional, one of `solid`, `dashed`, `dotted`) — Border line style.
        - `borderColor` (string, optional) — Border colour.
        - `borderRadius` (integer, optional, min 0, max 100) — Corner rounding in px, 0 to 100.
        - `textColor` (string, optional) — Text colour.
        - `linkColor` (string, optional) — Colour of links in the text.
        - `letterSpacing` (number, optional, min -2, max 20) — Space between letters in px, such as 3 for a spaced-out eyebrow line.
        - `textTransform` (string, optional, one of `none`, `uppercase`) — Set to `uppercase` to capitalise all text.
        - `lineHeight` (number, optional, min 1, max 3) — A multiple of the font size.
        - `fontWeight` (string, optional, one of `regular`, `bold`) — Text weight.
        - `fontStyle` (string, optional, one of `normal`, `italic`) — Text style.
        - `inset` (boolean, optional) — Draw the background and border as a box within the content width, not across the whole row.
      - `visibleIf` (object, optional) — Show the block only to contacts these segment rules match, checked as each message is rendered. Omit to show it to everyone. Not allowed on the footer.
        - One of: **Match all**
        - One of: **Match any**
      - `showOn` (string, optional, one of `mobile`, `desktop`) — Show the block only on phones (screens up to 620px) or only on wider screens. Clients that ignore screen size show the desktop version, and so does plain text. Omit to show it everywhere. Not allowed on the footer.
      - `columns` (object[][], optional, at most 3 items) — Nested child block arrays: exactly two for columns, three for columns3. Each child follows this block contract. Omit to retain legacy text columns.
      - `blocks` (object[], optional, at most 200 items) — A section's child blocks, drawn inside its box; or a repeat block's, written out once per item of its `list`, where `{{ item.<field> }}` reads that item. Repeats do not nest.
    - `theme` (EmailTheme, required)
      - `font` (string, optional, up to 200 characters) — Body font stack, such as `Inter, sans-serif`.
      - `hfont` (string, optional, up to 200 characters) — Heading font stack.
      - `bg` (string, optional) — Background of the email card, as hex.
      - `canvas` (string, optional) — Background behind the card, as hex.
      - `text` (string, optional) — Default text colour, as hex.
      - `accent` (string, optional) — Brand colour for buttons and highlights, as hex.
      - `link` (string, optional) — Link colour, as hex.
      - `width` (string, optional, one of `560px`, `600px`, `680px`) — Width of the email card on wide screens.
      - `radius` (string, optional, one of `0px`, `6px`, `12px`, `16px`, `24px`, `999px`) — Corner rounding of the email's card.
      - `btnRadius` (string, optional, one of `0px`, `6px`, `12px`, `16px`, `24px`, `999px`) — Button corners; `radius` rounds the email's card and, when this is unset, buttons.
      - `btn` (string, optional, one of `solid`, `outline`) — Button style.
    - `plainTextOverride` (string | null, optional, up to 512000 characters) — Plain text sent instead of the version generated from the blocks.
- `status` (string, required, one of `draft`, `scheduled`, `sending`, `sent`, `cancelled`) — Where the campaign is in its lifecycle.
- `sendAt` (string | null, required, date-time) — When it is scheduled to send, if at all.
- `localSendAt` (string | null, required) — Set for a local-time send: the wall-clock time each contact receives it at.
- `archivedAt` (string | null, required, date-time) — When it was archived; archived campaigns are left out of the list by default.
- `revision` (integer, required, min > 0, max 2147483647) — Raised by every edit. Send it back as `expectedRevision` on the update so a change made since you read it is refused instead of overwritten.
- `createdAt` (string, required, date-time)
- `updatedAt` (string, required, date-time)

## Example

```bash
curl -X POST https://api.kirora.io/v1/campaigns \
  -H "Authorization: Bearer $KIRORA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Weekend specials"
  }'
```

Response `201`:

```json
{
  "data": {
    "id": "018f2a4e-6b1c-7d3e-9f40-37386ae05a6b",
    "name": "Weekend specials",
    "channels": [
      "email"
    ],
    "groupId": "018f2a4e-6b1c-7d3e-9f40-532211a15a6b",
    "segmentId": "018f2a4e-6b1c-7d3e-9f40-243b225b5a6b",
    "audienceRules": {
      "all": [
        {
          "type": "attribute",
          "field": "field_example",
          "operator": "equals"
        }
      ]
    },
    "topicId": "018f2a4e-6b1c-7d3e-9f40-84e01f695a6b",
    "messageLimits": {
      "apply": true
    },
    "utm": {
      "enabled": true
    },
    "content": {},
    "status": "draft",
    "sendAt": "2030-03-14T09:30:00Z",
    "localSendAt": "localSendAt_example",
    "archivedAt": "2030-03-14T09:30:00Z",
    "revision": 1,
    "createdAt": "2030-03-14T09:30:00Z",
    "updatedAt": "2030-03-14T09:30:00Z"
  }
}
```
