---
title: Plan a campaign
description: Write down what you intend to run — channels, budgets, targeting, dates — as a structured Campaign Plan your agent can sum, approve, execute and record against.
---

The read tools tell an agent what _is_ running. The `campaign_plan_*` and `line_item_*` tools let it write down what you _intend_ to run — before anything is bought — in a form that adds up, that a human can approve, and that the mutation tools can then build from.

This is the document that usually lives in a spreadsheet: €120,000 across four channels and six markets, ten weeks, with an argument for the split. Here it is data, so nothing has to be retyped into an ad account by hand.

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

It is AdCrunch's own planning document, and one Campaign Plan may become several provider campaigns, or none at all. For campaigns that already exist on Meta or Google, use [`list_entities`](/mcp/tools/list-entities) and [`get_entity`](/mcp/tools/get-entity).

:::

## Three ideas

- A **Campaign Plan** is what your organization intends to run: an envelope, a currency, a window, optionally a brand, plus the `rationale` for the split and how `measurement` will judge it. It belongs to your organization, is authored incrementally, and is approved by a person.
- A **Line Item** is one row of that intent — a channel, an objective, an audience, an amount, a window. It holds only what stays **constant across everything it spawns**, which is why it carries no optimization goal and no campaign-tree shape.
- An **execution record** is one row per provider object a Line Item created. It is written by the agent that did the creating, and it is what later answers _"which of these campaigns came from row 3?"_

## The workflow

1. **Create the Campaign Plan**

    [`campaign_plan_create`](/mcp/tools/campaign-plan-create) needs a name, a
    one-line description and a **currency**. Everything else — the envelope, the
    window, the brand — can arrive later, because every amount written
    afterwards is read through that currency.

2. **Add the Line Items**

    One [`line_item_create`](/mcp/tools/line-item-create) per intent: channel,
    objective, and whatever of `budget`, `countries`, `age_min` / `age_max`,
    `gender` and dates you have decided. A line with no dates inherits the
    Campaign Plan's.

3. **Read it back**

    [`campaign_plan_get`](/mcp/tools/campaign-plan-get) returns the Line Items
    **in full** — there is no `line_item_get` — along with the money figures and
    four facts derived per line: `provider`, `executable`, the effective window,
    and `unit`. Read these back rather than assuming them.

4. **Approve, or validate one line**

    [`campaign_plan_approve`](/mcp/tools/campaign-plan-approve) is the human
    act: it approves the Campaign Plan and validates **every** draft line on it.
    When only one row is ready, use
    [`line_item_validate`](/mcp/tools/line-item-validate) so your approval does
    not reach somebody else's half-written row.

5. **Execute with the mutation tools**

    Creating the campaigns and ad sets is the ordinary write path —
    [`meta_create_campaign`](/mcp/tools/meta-create-campaign),
    [`meta_create_adset`](/mcp/tools/meta-create-adset) — against the
    `advertiser_id` on the line, and it needs the `mutation:write` scope.

6. **Record what you created**

    After each create, call
    [`line_item_record_execution`](/mcp/tools/line-item-record-execution) with
    the native id. Nothing does this for you: one line may create any number of
    objects and you chose the shape, so only you know which came from where.

## Three money figures, not one

`campaign_plan_get` returns an `allocation` block. The numbers in it answer different questions and are allowed to disagree.

| Figure | What it is |
| --- | --- |
| `totalBudget` | The **envelope** — what you have. `null` means no declared constraint, never zero. |
| `allocated` | The sum of the Line Item budgets. |
| `unallocated` | The envelope minus the allocated sum — what is still unplaced. |
| `comparable` | Whether that sum means anything at all. |

:::warning[Amounts are integer minor units]

Send `4050` for €40.50, never `40.50`. A fractional amount is refused rather than rounded — rounding would silently turn €40.50 into 40 cents. The currency lives on the Campaign Plan; a Line Item carries an amount only.

:::

:::info[A mixed-unit plan has no sum]

The unit is derived: a line with an effective end date holds a **total** for that period, one without holds a **daily** rate. Mix the two and `comparable` comes back `false` with `unallocated: null` — an envelope minus a rate is not a quantity. Say so rather than printing a total that looks right and is not.

:::

## Statuses you'll see

| On | Value | Meaning |
| --- | --- | --- |
| **Campaign Plan** | `draft` | Being authored. Editable, and not approved by anyone. |
| **Campaign Plan** | `approved` | A person signed off on the content at a specific revision. |
| **Line Item** | `draft` | Not cleared to run. |
| **Line Item** | `validated` | Cleared to run — by its own validation, or by the approval that cascaded over it. |

:::tip[Editing reverts, and only locally]

Edit a Line Item and that line returns to `draft`. Edit the Campaign Plan's own fields, or add a line, and the Campaign Plan returns to `draft`. What has already been created on a provider is **untouched** — reverting a line changes your plan, never your delivery.

:::

## Before you execute

Approval is a gate on the plan, not a lock on the ad account: the Meta tools know nothing about your Campaign Plan and will happily create from a row nobody validated. Check these yourself, from the `campaign_plan_get` response, before you create anything:

- **The line is `validated`.** This is the gate; walking through it is your job.
- **`executable` is `true`.** A channel nobody has connected yet still plans, sums and approves — it simply cannot be built.
- **The `advertiser_id` is set, owned by your organization, and on the line's `provider`.**
- **The account currency equals the Campaign Plan's.** AdCrunch **never converts** — Meta reads `daily_budget` in the _account's_ currency, so a EUR plan built into a USD account creates the same number in dollars. A converted budget is a different plan.
- **The window resolves**, after inheritance, and starts before it ends.
- **Send the budget in the unit the line says.** `total` is a lifetime budget; `daily` is a rate.

:::warning[A line that ran but was never recorded looks unrun]

It is already spending while the plan says nothing came of it. Record each object as you create it, and report **"3 objects created"** rather than "complete" — how many objects a line _should_ produce was never declared, so completeness is not knowable.

:::

## Scopes

Authoring needs `campaign_plan:read` and `campaign_plan:write`; recording an execution is a write on the plan, so it needs `campaign_plan:write` too. Actually creating campaigns needs `mutation:write`, granted separately.

That separation is the point: an agent can plan an entire quarter — restructure it, re-budget it, approve nothing — without being able to move a cent. See [Auth & scopes](/mcp/auth).

## Channels and providers

Ten channels can be planned: `meta`, `tiktok`, `snapchat`, `google_search`, `google_pmax`, `google_display`, `youtube`, `programmatic_display`, `linkedin` and `x`. Your media plan already spans them, and a planning surface that covered one would be a planning surface you kept a spreadsheet beside.

Execution is a different question, and a narrower one: `executable` is `true` for **Meta** today. It is derived per request from what AdCrunch can write to, never stored — as more providers come online, the same rows become executable with nothing to migrate.

## Next

**[campaign_plan_create](/mcp/tools/campaign-plan-create)**

Start the plan. Currency first, everything else later.

**[campaign_plan_get](/mcp/tools/campaign-plan-get)**

The lines in full, the money, and what each has created.

**[line_item_record_execution](/mcp/tools/line-item-record-execution)**

Close the loop after every create.
