# Duplicate a campaign

`POST /v1/campaigns/{id}/duplicate`

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

Copies an existing campaign, including its content snapshot and audience selection, into a new `draft`. Statistics and delivery history are not copied.

## Path parameters

- `id` (string, required, uuid)

## 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/018f2a4e-6b1c-7d3e-9f40-37386ae05a6b/duplicate \
  -H "Authorization: Bearer $KIRORA_API_KEY"
```

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"
  }
}
```
