---
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, the rationale and the measurement can come later. 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 and a currency are required — the window, the budget envelope and the brand are all authored incrementally. 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_entities and get_entity.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `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. |
| `measurement` | string | no | How success will be judged. |
| `name` | string | yes | Display name, e.g. "Q4 Acquisition". |
| `rationale` | string | no | The argument for the split — why this money goes to these channels. It is what a reviewer approves. |
| `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.brandId` | string or null | yes | The Brand that the Campaign Plan is for, `brd_…`, or `null`. |
| `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. `get_entity` and `list_entities` 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[].mutationWorkflowId` | string or null | yes | The `workflowId` 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`, `line_item_delete` or `line_item_validate`. |
| `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 cleared the Line Item to run, by `line_item_validate` or by the approval of the Campaign Plan. 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.measurement` | string or null | yes | How the result is judged, or `null`. |
| `campaignPlan.name` | string | yes | The display name. |
| `campaignPlan.rationale` | string or null | yes | The argument for the split of the money, or `null`. A reviewer approves it. |
| `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`, `campaign_plan_delete` or `campaign_plan_approve`. 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. 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`
- `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.
- **Destructive.** The tool can make a change that you cannot undo. A client can ask you to confirm before it calls the tool.
- **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
{
  "brand_name": "acme",
  "currency": "EUR",
  "description": "The spring sale of Acme, on Meta, in France and Belgium.",
  "end_date": "2026-05-31",
  "measurement": "Revenue in the window, against the spring of 2025.",
  "name": "Spring Sale 2026",
  "rationale": "Meta carries the reach. One Line Item keeps the budget in one place.",
  "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
    },
    "brandId": "brd_6a9c33",
    "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": [],
    "measurement": "Revenue in the window, against the spring of 2025.",
    "name": "Spring Sale 2026",
    "rationale": "Meta carries the reach. One Line Item keeps the budget in one place.",
    "revision": 1,
    "slug": "spring-sale",
    "startDate": "2026-03-01",
    "status": "draft",
    "totalBudget": 5000,
    "updatedAt": null
  }
}
```
