---
search:
  tags:
    - Campaign Plans
    - POST
seo:
  description: >-
    Duplicates a Campaign Plan: one new Campaign Plan, with a copy… Reference
    for the POST /context/campaign-plans/{slug}/duplicate endpoint in the
    AdCrunch API.
sidebar:
  label: Duplicate a Campaign Plan
  badge: POST
title: Duplicate a Campaign Plan
type: openapi-operation
---
Duplicates a Campaign Plan: one new Campaign Plan, with a copy of each of its Line Items. Use it to run the same plan again, for example each month.

The copy keeps the name, the summary, the body, the currency, the envelope, the window and the Brand. Each Line Item keeps its name, channel, objective, countries, age range, gender, Persona, ad account, budget, window and target. The copy keeps the dates: edit them after the duplicate.

The copy does not keep the Executions, the Comments, the Assignees, the Favorites or the approval. The copy and each Line Item start in draft, at `revision` 1. A deleted Brand or a deleted Persona is not copied. The source does not change.

One write holds the copy and its Line Items. When it fails, no copy stays.

`POST /context/campaign-plans/{slug}/duplicate`

**Request body** (`application/json`, required)

- `name` (string) — The name of the copy. Omit for the name of the source followed by " (copy)", cut to 120 characters.
- `slug` (string) — The slug of the copy; normalized to kebab-case. Omit to derive it from the name: when that slug is taken, the copy takes \`-2\`, then \`-3\`, up to the first free one. A slug that you send and that is taken is \`409\`.

Request body example:

```json
{
  "name": "Spring Sale 2027"
}
```

**Responses**

- `201` — The copy, in \`draft\`, at \`revision\` 1. \`lineItemCount\` counts the copied Line Items. List them with \`GET /campaign-plans/{slug}/line-items\`.
- `400` — The request does not match the schema of this operation: a field is missing or has the wrong type, or the body is not valid JSON. \`error\` is \`invalid\_request\`, and \`issues\` names each field. Nothing was changed.
- `401` — No API key, or one that does not resolve. See the security scheme. \`error\` is \`unauthorized\`.
- `402` — The organization has no active subscription. An owner subscribes at \`paywallUrl\`. \`error\` is \`organization\_unentitled\`.
- `403` — The caller does not hold \`campaign\_plan:write\`. \`error\` is \`forbidden\`.
- `404` — No Campaign Plan with that slug. \`error\` is \`not\_found\`.
- `409` — The \`slug\` that you sent is taken. \`error\` is \`slug\_conflict\`.
- `422` — \`invalid\_slug\`: the name or the slug holds no letter or digit.

Response example, 201:

```json
{
  "allocation": {
    "allocated": 2500,
    "comparable": true,
    "unallocated": 2500,
    "unit": "total"
  },
  "assignees": [],
  "body": "## Why this split\n\nMeta carries the reach. One Line Item keeps the budget in one place.\n\n## How it will be judged\n\nRevenue in the window, against the spring of 2025.",
  "brand": {
    "name": "Acme",
    "slug": "acme"
  },
  "brandId": "brd_6a9c33",
  "commentCount": 0,
  "createdAt": 1769040000000,
  "createdBy": "usr_4d8b12",
  "currency": "EUR",
  "description": "The spring sale of Acme, on Meta, in France and Belgium.",
  "endDate": "2026-05-31",
  "id": "cpl_9b3f51",
  "lineItemCount": 1,
  "name": "Spring Sale 2026 (copy)",
  "revision": 1,
  "slug": "spring-sale-2026-copy",
  "startDate": "2026-03-01",
  "status": "draft",
  "totalBudget": 5000,
  "updatedAt": null
}
```
