---
title: campaign_plan_create
description: 'Write down what you intend to run: the channels, the audience, the money and the dates.'
---

Ask the agent to write down what you intend to run. It creates a Campaign Plan as a draft, and it buys nothing.

> "Create a Campaign Plan for the Acme spring sale: €5,000 in euros, from 1 March to 31 May 2026."

The agent calls `campaign_plan_create`. You see the new Campaign Plan, its slug and its figures. The brand, the envelope, the dates and the body can come later. The body is one markdown document: why this split, and how the result will be judged. Each amount is in whole units of the currency: you give euros, not cents.

[Plan a campaign](/mcp/tools/plan-a-campaign) walks the whole job.

## Reference

Create a Campaign Plan: what this organization intends to run, on which channels, for whom, for how much, over what period. Only a name, a description (a one-line summary) and a currency are required — the window, the budget envelope, the brand and the body are all authored incrementally. The body is one markdown document: why this money goes to these channels, and how success will be judged. Amounts are whole units of the plan’s currency: send 40.5 for €40.50, never 4050. The plan starts as a draft. A Campaign Plan is AdCrunch's own planning document — it is **not** a campaign on Meta or Google. For those, use list_campaigns and get_campaign.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `body` | string, 1 to 50000 characters | no | The long markdown document of the plan: why this money goes to these channels, how success will be judged, and anything else a reviewer reads. It is what a person approves. Up to 50,000 characters. |
| `brand_name` | string | no | The slug of the brand this plan is for. Optional and changeable later — a plan may precede its brand, or cover several. |
| `currency` | string, 3 characters | yes | ISO 4217, e.g. EUR. Changeable until a Line Item carries a budget, frozen after that. |
| `description` | string | yes | One-line summary used to pick this plan from a list. |
| `end_date` | string | no | Omit for an always-on plan. Line Items inherit this when they set no end of their own — and a line with no effective end reads as a daily rate. |
| `name` | string | yes | Display name, e.g. "Q4 Acquisition". |
| `slug` | string | no | Optional explicit slug handle; normalized to kebab-case. Defaults to one derived from the name. |
| `start_date` | string | no |  |
| `total_budget` | number, at least 0 | no | The envelope: what you have, in whole units of the plan currency (40.5 is 40.50). Omit for "no declared constraint" — which is not the same as zero. |

### Output

A successful call returns this object in `structuredContent`.

| Field | Type | Always present | Description |
| --- | --- | --- | --- |
| `campaignPlan` | object | yes | The Campaign Plan as it is now, with its Line Items in full. |
| `campaignPlan.allocation` | object | yes | The money of the Campaign Plan. AdCrunch calculates it on each read and does not store it. |
| `campaignPlan.allocation.allocated` | number or null | yes | The sum of the Line Item budgets, in whole units of the currency. `null` when the Line Items with a budget do not share a unit. |
| `campaignPlan.allocation.comparable` | boolean | yes | `false` when the Line Items with a budget mix totals and daily rates. Then no sum exists: say "not comparable", and do not add the budgets. |
| `campaignPlan.allocation.unallocated` | number or null | yes | The envelope less the allocated sum. It can be negative. `null` when the envelope is not set, when the Line Items do not share a unit, or when the unit is `daily`. |
| `campaignPlan.allocation.unit` | one of `total`, `daily` or null | yes | The unit that the Line Items with a budget share. `null` when no Line Item has a budget, or when the Line Items do not share a unit. |
| `campaignPlan.assignees` | array of object | yes | The current members who are accountable for the record, oldest first, at most 10. An Assignee grants no access. |
| `campaignPlan.assignees[].id` | string | yes | The `usr_…` id of the member. |
| `campaignPlan.assignees[].name` | string | yes | The name of the member. |
| `campaignPlan.body` | string or null | yes | The long markdown document of the Campaign Plan: the argument for the split, how the result is judged, and anything else a reviewer reads. `null` when nobody wrote it. |
| `campaignPlan.brand` | object or null | yes | The Brand that the Campaign Plan is for, or `null` when it has no Brand or its Brand was deleted. |
| `campaignPlan.brand.name` | string | yes | The display name of the Brand. |
| `campaignPlan.brand.slug` | string | yes | The slug of the Brand. Each tool takes it as `brand_name`. |
| `campaignPlan.brandId` | string or null | yes | The Brand that the Campaign Plan is for, `brd_…`, or `null`. |
| `campaignPlan.commentCount` | number | yes | The number of live Comments on the Campaign Plan, replies included. List them with `campaign_plan_comment_list`. |
| `campaignPlan.createdAt` | number | yes | Milliseconds since the Unix epoch, UTC. |
| `campaignPlan.createdBy` | string | yes | The user who created the Campaign Plan, `usr_…`. |
| `campaignPlan.currency` | string | yes | ISO 4217. Each amount of the Campaign Plan and of its Line Items is in whole units of this currency. It cannot change after a Line Item has a budget. |
| `campaignPlan.description` | string | yes | One sentence that tells the Campaign Plan apart in a list. |
| `campaignPlan.endDate` | string or null | yes | The last day of the Campaign Plan, or `null`. With no end date, the Campaign Plan is always on, and the budget of a Line Item with no end date of its own is a daily rate. |
| `campaignPlan.id` | string | yes | The id of the Campaign Plan, `cpl_…`. |
| `campaignPlan.lineItems` | array of object | yes | Each Line Item of the Campaign Plan, in full. |
| `campaignPlan.lineItems[].advertiserId` | string or null | yes | The ad account that the Line Item runs through, `acc_…`, or `null`. |
| `campaignPlan.lineItems[].ageMax` | number or null | yes | The highest age, or `null` for no upper bound. |
| `campaignPlan.lineItems[].ageMin` | number or null | yes | The lowest age, or `null` for no lower bound. |
| `campaignPlan.lineItems[].budget` | number or null | yes | The amount in whole units of the currency of the Campaign Plan, or `null` when it is not set. `unit` tells how to read it. |
| `campaignPlan.lineItems[].campaignPlanId` | string | yes | The id of the Campaign Plan that holds the Line Item, `cpl_…`. |
| `campaignPlan.lineItems[].channel` | one of `meta`, `tiktok`, `snapchat`, `google_search`, `google_pmax`, `google_display`, `youtube`, `programmatic_display`, `linkedin`, `x` | yes | What the Line Item buys, in planning words. A channel is not a provider. |
| `campaignPlan.lineItems[].countries` | array of string | yes | ISO 3166-1 alpha-2 codes. An empty list means that the geography is not decided. |
| `campaignPlan.lineItems[].createdAt` | number | yes | Milliseconds since the Unix epoch, UTC. |
| `campaignPlan.lineItems[].createdBy` | string | yes | The user who added the Line Item, `usr_…`. |
| `campaignPlan.lineItems[].effectiveEndDate` | string or null | yes | The end that applies: the end date of the Line Item, else the end date of the Campaign Plan. `null` when neither has one. |
| `campaignPlan.lineItems[].effectiveStartDate` | string or null | yes | The start that applies: the start date of the Line Item, else the start date of the Campaign Plan. `null` when neither has one. |
| `campaignPlan.lineItems[].endDate` | string or null | yes | The end date of the Line Item itself, or `null` when the Line Item uses the end date of the Campaign Plan. |
| `campaignPlan.lineItems[].executable` | boolean | yes | `true` when AdCrunch can create objects for this channel today. It is not a promise that a create succeeds. |
| `campaignPlan.lineItems[].executions` | array of object | yes | The provider objects that the Line Item created, as `line_item_record_execution` recorded them. The list does not tell whether the Line Item is complete. |
| `campaignPlan.lineItems[].executions[].advertiserId` | string | yes | The ad account that holds the object, `acc_…`. |
| `campaignPlan.lineItems[].executions[].createdAt` | number | yes | When AdCrunch recorded the object, in milliseconds since the Unix epoch, UTC. |
| `campaignPlan.lineItems[].executions[].entityId` | string | yes | The id of the object on the provider, with no prefix. The entity tools, such as `list_campaigns` and `get_ad`, use the same id. |
| `campaignPlan.lineItems[].executions[].entityType` | string | yes | The word of the provider for the level of the object, such as `campaign`, `adset` or `ad`. |
| `campaignPlan.lineItems[].executions[].id` | string | yes | The id of the record, `exe_…`. |
| `campaignPlan.lineItems[].executions[].mutationId` | string or null | yes | The `mutationId` of the change that created the object, or `null` when the record names none. |
| `campaignPlan.lineItems[].executions[].provider` | one of `meta`, `gads`, `tiktok`, `x`, `openai`, `snapchat`, `dv360` | yes | The provider of the object. |
| `campaignPlan.lineItems[].gender` | one of `all`, `men`, `women` or null | yes | The gender of the audience, or `null` when it is not decided. `null` executes as `all`. |
| `campaignPlan.lineItems[].id` | string | yes | The id of the Line Item, `lni_…`. |
| `campaignPlan.lineItems[].objective` | one of `awareness`, `engagement`, `leads`, `sales`, `traffic`, `app_promotion` | yes | What the Line Item is for. |
| `campaignPlan.lineItems[].personaId` | string or null | yes | The Persona that explains the audience, `psn_…`, or `null`. The countries, the ages and the gender are what execute. |
| `campaignPlan.lineItems[].provider` | one of `meta`, `gads`, `tiktok`, `x`, `openai`, `snapchat`, `dv360` or null | yes | The provider that sells the channel, or `null` when AdCrunch has no integration for it. |
| `campaignPlan.lineItems[].revision` | number | yes | The revision of the Line Item. It increments when the Line Item changes. Send it as `base_revision` to `line_item_update` or `line_item_delete`. |
| `campaignPlan.lineItems[].startDate` | string or null | yes | The start date of the Line Item itself, or `null` when the Line Item uses the start date of the Campaign Plan. |
| `campaignPlan.lineItems[].status` | one of `draft`, `validated` | yes | `validated` when a person validated the Line Item in the AdCrunch console, alone or with the approval of the Campaign Plan. It is a record, not a gate: the provider write tools do not check it. A real change to the Line Item returns it to `draft`. |
| `campaignPlan.lineItems[].unit` | one of `total`, `daily` | yes | How to read `budget`. `total` when the Line Item has an effective end date: the budget is for the whole period. `daily` when it has none: the budget is for each day. |
| `campaignPlan.lineItems[].updatedAt` | number or null | yes | When the Line Item last changed, or `null` when it never changed. |
| `campaignPlan.name` | string | yes | The display name. |
| `campaignPlan.revision` | number | yes | The revision of the Campaign Plan. It increments when the Campaign Plan changes. Send it as `base_revision` to `campaign_plan_update` or `campaign_plan_delete`. A Line Item has a revision of its own. |
| `campaignPlan.slug` | string | yes | The slug of the Campaign Plan. Each tool takes it as `campaign_plan_name`. |
| `campaignPlan.startDate` | string or null | yes | The first day of the Campaign Plan, or `null`. |
| `campaignPlan.status` | one of `draft`, `approved` | yes | `approved` when a person approved the content at this revision in the AdCrunch console. It is a record, not a gate: the provider write tools do not check it. A real change to the Campaign Plan, or a Line Item that is added or deleted, returns it to `draft`. |
| `campaignPlan.totalBudget` | number or null | yes | The envelope: the money that you have, in whole units of `currency`. `null` means no declared limit, not zero. |
| `campaignPlan.updatedAt` | number or null | yes | When the Campaign Plan last changed, or `null` when it never changed. |

### Failure codes

A failed call has `isError` set, and `structuredContent.error` holds one of these codes. [Errors](/mcp/errors) describes the shape of a failed call.

- `not_found`
- `invalid_slug`
- `invalid_currency`
- `invalid_amount`
- `invalid_window`
- `slug_conflict`
- `organization_unentitled`
- `forbidden`
- `invalid_request`
- `internal_error`

### Scope

The token must hold `campaign_plan:write`. [Auth & scopes](/mcp/auth) lists each scope.

### Annotations

A client reads these hints. A hint that the tool does not declare has the default value of the MCP specification.

- **Writes.** The tool can change data.
- **Not destructive.** The tool makes only additive changes.
- **Not idempotent.** A second call with the same arguments can change more.
- **Closed world.** The tool reads and writes the data of AdCrunch only.

### Example

The arguments:

```json
{
  "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",
  "currency": "EUR",
  "description": "The spring sale of Acme, on Meta, in France and Belgium.",
  "end_date": "2026-05-31",
  "name": "Spring Sale 2026",
  "slug": "spring-sale",
  "start_date": "2026-03-01",
  "total_budget": 5000
}
```

The result, in `structuredContent`:

```json
{
  "campaignPlan": {
    "allocation": {
      "allocated": 0,
      "comparable": true,
      "unallocated": 5000,
      "unit": null
    },
    "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": 2,
    "createdAt": 1768435200000,
    "createdBy": "usr_5d2b71",
    "currency": "EUR",
    "description": "The spring sale of Acme, on Meta, in France and Belgium.",
    "endDate": "2026-05-31",
    "id": "cpl_4e1a08",
    "lineItems": [],
    "name": "Spring Sale 2026",
    "revision": 1,
    "slug": "spring-sale",
    "startDate": "2026-03-01",
    "status": "draft",
    "totalBudget": 5000,
    "updatedAt": null
  }
}
```
