---
title: Plan a campaign
description: Ask your agent to write down what you intend to run, approve it in the AdCrunch console, build it on Meta, record what it created, and read what it delivered.
---

A **Campaign Plan** is what you intend to run, before you buy anything: the channels, the money, the audience and the dates. You ask your agent to write it. A person approves it in the AdCrunch console. Then the agent can build it on Meta and record each object that it creates. This page walks the job from the first prompt to the last record.

:::warning[A Campaign Plan is not a campaign on a provider]

A Campaign Plan is a planning document of AdCrunch. One Campaign Plan can become many campaigns on Meta, or none. For the campaigns that run on Meta or Google now, ask for [`list_campaigns`](/mcp/tools/list-campaigns) and [`get_campaign`](/mcp/tools/get-campaign).

:::

## Before you start

- **Scopes.** The token of your client must hold `campaign_plan:read` to read Campaign Plans and `campaign_plan:write` to write them. No MCP tool approves a Campaign Plan. To build on Meta, the token must also hold `mutation:write`. To read what a Campaign Plan delivered, it must also hold `observe:read`. [Auth & scopes](/mcp/auth) explains each scope.
- **A connected ad account.** To build a Line Item, your organization must have a connected Meta ad account. Today, AdCrunch builds on Meta only. [What each provider supports](/connect/providers) tells where each other channel stops.

## Write the Campaign Plan

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

The agent calls [`campaign_plan_create`](/mcp/tools/campaign-plan-create). Its page tells what the agent must send and what can come later. The new Campaign Plan is a `draft`. You see its name, its slug and its figures.

:::warning[Amounts are in whole units]

Each amount is in whole units of the currency of the Campaign Plan: `40.5` is €40.50, and `4050` is €4,050.00. AdCrunch refuses an amount that has more decimals than the currency has. It does not round it.

:::

## Add the Line Items

> "Add a Line Item to the spring sale Campaign Plan: Meta, for sales, €2,500, in France and Belgium, ages 25 to 44, through our Meta ad account."

The agent calls [`line_item_create`](/mcp/tools/line-item-create) once for each thing that you buy. Its page tells what the agent must send. You see the new Line Item, and two facts that AdCrunch calculates for it:

- **Can AdCrunch build it today?** You can plan ten channels, from Meta and TikTok to LinkedIn and programmatic display. Today, AdCrunch builds a Line Item on Meta only. A Line Item on another channel stays in the Campaign Plan and adds to its money, but the agent cannot build it.
- **Is its budget a total or a daily rate?** A Line Item with no dates of its own uses the dates of the Campaign Plan. When the Line Item has an end date, its budget is a total for the period. When it has no end date, its budget is a rate for each day.

To say how a Line Item is judged, give it a target: one metric and one value.

> "Give the Meta Line Item a target: a CPA of at most €25."

The metric is `cpa`, `cpc`, `cpm` or `roas`. A cost target is in whole units of the currency of the Campaign Plan, and the Line Item meets it at or below that value. A `roas` target is a ratio, such as `3.5`, and the Line Item meets it at or above that value. A target blocks nothing: AdCrunch does not check it before an approval or a build. A cost target freezes the currency of the Campaign Plan, as a budget does.

A persona explains the audience to the people who read the Campaign Plan. The countries, the ages and the gender are what execute. A Line Item can name only a persona of the brand of its Campaign Plan.

A new Line Item starts as a `draft`. When you add a Line Item to an approved Campaign Plan, the Campaign Plan goes back to `draft`.

## Read the Campaign Plan back

> "Show me the spring sale Campaign Plan. How much of the envelope is still free?"

The agent calls [`campaign_plan_get`](/mcp/tools/campaign-plan-get). It gets each Line Item in full, with what each Line Item created. To find a Campaign Plan first, the agent calls [`campaign_plan_list`](/mcp/tools/campaign-plan-list), which gives the figures of each Campaign Plan, newest first.

You see three money figures. They answer three different questions:

| Figure | What it tells you |
| --- | --- |
| The envelope | The money that you have. No envelope means no declared limit. It does not mean zero. |
| The allocated sum | The sum of the Line Item budgets. |
| What is left | The envelope less the allocated sum. It can be negative: AdCrunch shows an over-allocation and does not block it. When the Line Items hold daily rates, AdCrunch does not calculate it, because a total less a rate is not a quantity. |

:::info[Some Campaign Plans have no sum]

When some Line Items hold totals and others hold daily rates, no sum of them exists. Then the agent tells you that the figures are "not comparable". It does not add them.

:::

## Post an update

> "Post on the spring sale plan that Meta reached the CPA target."

The agent calls [`campaign_plan_comment_create`](/mcp/tools/campaign-plan-comment-create). The Comment shows in the Updates tab of the Campaign Plan, with the name of the agent. To read what the team posted, the agent calls [`campaign_plan_comment_list`](/mcp/tools/campaign-plan-comment-list).

## Change the Campaign Plan

> "Move the end of the spring sale Campaign Plan to 30 June, and raise the envelope to €7,500."

The agent reads the Campaign Plan, then calls [`campaign_plan_update`](/mcp/tools/campaign-plan-update) with the fields that change. For a Line Item, it calls [`line_item_update`](/mcp/tools/line-item-update) or [`line_item_delete`](/mcp/tools/line-item-delete). To remove the whole Campaign Plan and its Line Items, it calls [`campaign_plan_delete`](/mcp/tools/campaign-plan-delete). These guardrails apply:

- **An edit cannot overwrite an edit.** The Campaign Plan has a revision, and each Line Item has a revision of its own. `campaign_plan_update` and `campaign_plan_delete` send the revision of the Campaign Plan that the agent read. `line_item_update` and `line_item_delete` send the revision of the Line Item that the agent read. When the Campaign Plan or the Line Item changed after the agent read it, AdCrunch refuses the write with `revision_mismatch`. The agent reads again, checks that the two changes agree, and tries again. [What to expect](/mcp/what-to-expect#an-edit-can-collide-with-another-edit) states this rule for each object that has a revision.
- **A change cancels the approval of what it changes.** A real change to an approved Campaign Plan returns it to `draft`. So does a Line Item that is added or deleted. A real change to a validated Line Item returns that Line Item to `draft`, and the approval of the Campaign Plan stays. A write that changes nothing cancels nothing.
- **The currency freezes at the first cost.** When a Line Item has a budget, or a cost target (`cpa`, `cpc` or `cpm`), AdCrunch refuses a change of currency. AdCrunch never converts an amount.
- **Nothing live changes.** An edit or a delete changes the Campaign Plan only. A campaign that a Line Item created stays on Meta as it is.

## Approve in the AdCrunch console

An agent cannot approve a Campaign Plan, and it cannot validate a Line Item. A person does both in the AdCrunch console, on the page of the Campaign Plan:

- **Approve plan** approves the Campaign Plan at the revision that you see. It also validates each draft Line Item of the Campaign Plan.
- **Validate** on one Line Item validates that Line Item only. Use it when one Line Item is ready and another person still writes the others.

The agent wrote the Campaign Plan, so a person approves it. When the Campaign Plan is ready, the agent tells you to approve it in the AdCrunch console.

:::info[An approval is a record]

An approval records that a person agreed to the content of the Campaign Plan. It creates nothing on Meta, and it spends nothing. It does not block on an incomplete or over-allocated Campaign Plan: the person who approves decides. The Meta tools do not check the approval, so AdCrunch does not refuse to build a Line Item that is still a `draft`. An approval changes the revision of the Campaign Plan and of each Line Item that it validates, so the agent reads the Campaign Plan again before its next write.

:::

## Build it on Meta

> "Build the Meta Line Item of the spring sale Campaign Plan."

The agent reads the Campaign Plan, and then builds the Line Item with the Meta tools, such as [`meta_create_campaign`](/mcp/tools/meta-create-campaign) and [`meta_create_adset`](/mcp/tools/meta-create-adset). Each create arrives paused, so nothing spends until you activate it. AdCrunch queues each create, and the agent follows it with [`get_mutation_status`](/mcp/tools/get-mutation-status). When the change completes, that tool gives the id of the new object.

The Meta tools do not read your Campaign Plan, and they do not check its approval. So ask the agent to check that the Line Item is ready before it builds. The agent calls [`line_item_readiness`](/mcp/tools/line-item-readiness), which gives the reason and the fix when the Line Item is not ready. A Line Item is ready when each of these is true:

- The Line Item is `validated`.
- AdCrunch can build its channel today.
- The Line Item names an ad account that your organization connected, on the provider of its channel.
- AdCrunch knows the currency of that ad account. When the currency is unknown, AdCrunch refuses the Line Item: reconnect the provider to refresh the currency.
- The currency of the ad account is the currency of the Campaign Plan. AdCrunch never converts: a €2,500 budget sent to a USD account becomes $2,500.
- The Line Item has a start date, an end date, or both, of its own or from the Campaign Plan. When it has both, the start is on or before the end.

The budget then goes to Meta in the unit of the Line Item: a total as a lifetime budget, a daily rate as a daily budget.

## Record what the agent created

When a create completes, the agent calls [`line_item_record_execution`](/mcp/tools/line-item-record-execution) with the id that `get_mutation_status` gave for the new object. Then the Line Item shows what it created, and you can see which campaigns came from which Line Item.

:::warning[Nothing records this for you]

One Line Item can create many objects, and only the agent knows which object came from which Line Item. A Line Item that created a campaign but has no record looks unbuilt while it can already spend. Ask the agent to record each object after each create.

:::

A second record of the same object adds nothing. Nobody declares how many objects a Line Item must create, so the agent reports "3 objects created", and never "complete".

In the AdCrunch console, the **Line Items** tab of the Campaign Plan shows the same count for each Line Item. Click the count to open the list of the objects. Each row shows the level, the name, the status and the day of the record. A row whose object AdCrunch does not store yet shows the provider id in place of the name.

To stop what a Line Item created, ask the agent to pause or archive it with [`meta_update_status`](/mcp/tools/meta-update-status). A delete of the Line Item or of the Campaign Plan does not stop it.

## Read what it delivered

> "How is the spring sale Campaign Plan pacing against its budget?"

The agent calls [`campaign_plan_delivery`](/mcp/tools/campaign-plan-delivery). AdCrunch adds up the insights of the objects that each Line Item recorded. It calculates the answer each time that the agent asks, and it stores nothing. For each Line Item, you see:

| Figure | What it tells you |
| --- | --- |
| The sums | The spend, the impressions, the clicks, the conversions and the conversion value, from the start date that applies to today, or to the end date when it is in the past. The CPA, the CPC, the CPM and the ROAS come from these sums, never from a mean of the days. |
| The pacing | The spend against the spend that the budget expects by today. For a total budget, the expected spend is the budget times the days that elapsed, divided by the days of the Line Item. For a daily budget, it is the rate times the days that elapsed. Before the start, AdCrunch expects nothing. |
| The target | The target, the same metric delivered, and whether the Line Item meets it. A cost meets its target at or below it. A ROAS meets its target at or above it. |
| The last day | The most recent day that the sums include. AdCrunch reads the insights once a day, and conversions keep arriving after the click. |

These rules apply:

- **The pacing is a ratio.** `0.8` means that the Line Item spent 80 % of what its budget expects by today. AdCrunch does not say "under" or "over": you decide with the agent what is too low or too high.
- **Each object counts once.** A campaign holds the spend of its ad sets. When a Line Item records a campaign and an ad set of that campaign, AdCrunch counts only the campaign.
- **No delivery is not zero.** A Line Item that recorded no object shows "nothing recorded". A recorded object that has no insight yet shows "no data yet".
- **AdCrunch never converts a currency.** When an object spends in another currency than the Campaign Plan, the money of its Line Item is "not comparable", and the answer gives the currencies.
- **A deleted object still counts.** A record is a fact: an object that was archived or deleted later still counts for the days that it spent.

The Campaign Plan also gives a total, over the dates of the Campaign Plan. The total counts each object once: when two Line Items record the same campaign, or one records a campaign and another an ad set of that campaign, the money counts once. So the total can be less than the sum of the Line Items. When the Line Items do not share a unit, or when an object spends in another currency, the agent tells you that the figures are "not comparable".

## When a call fails

A failed call carries a code and a sentence. The sentence tells the agent what to do next: for example, to read the Campaign Plan again after `revision_mismatch`. [Errors](/mcp/errors) describes the shape of a failed call. Each tool page lists the codes of its tool.
