campaign_plan_create
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 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.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.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 describes the shape of a failed call.
not_foundinvalid_sluginvalid_currencyinvalid_amountinvalid_windowslug_conflictorganization_unentitledforbiddeninvalid_requestinternal_error
Scope
The token must hold campaign_plan:write. Auth & scopes 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:
{
"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:
{
"campaignPlan": {
"allocation": {
"allocated": 0,
"comparable": true,
"unallocated": 5000,
"unit": null
},
"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",
"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
}
}