---
title: campaign_plan_delivery
description: 'Read what each Line Item of a Campaign Plan delivered: its spend and results, its pacing against its budget, and its target.'
---

Ask the agent how a Campaign Plan runs against what you planned. The agent adds up the insights of the objects that each Line Item recorded, so you do not compare figures by hand.

> "How is the spring sale Campaign Plan pacing? Does the Meta Line Item meet its CPA target?"

The agent calls `campaign_plan_delivery`. For each Line Item, the answer gives:

- **The sums** of spend, impressions, clicks, conversions and conversion value, from the start date that applies to today, or to the end date when it is in the past. AdCrunch calculates the CPA, the CPC, the CPM and the ROAS from these sums.
- **The pacing**: the spend against the spend that the budget expects by today. A total budget spreads over its dates, day by day. A daily budget is the rate times the days. The pacing is a ratio, such as `0.8`. AdCrunch does not say "under" or "over": you decide what is too low or too high.
- **The target** against the same metric, delivered, and whether the Line Item meets it.
- **The last day** that the sums include. AdCrunch reads the insights once a day, and conversions keep arriving after the click.

AdCrunch counts each object once: a campaign holds the spend of its ad sets, so an ad set recorded with its campaign does not add its spend again. A Line Item that recorded no object answers "nothing recorded", not a spend of 0. AdCrunch never converts a currency: when an object spends in another currency, the money of its Line Item is "not comparable".

The token must hold `campaign_plan:read` and `observe:read`.

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

## Reference

Read what each Line Item of a Campaign Plan delivered: the sums of the provider objects that it recorded with line_item_record_execution, its pacing against its budget, and its target against the delivered value. It reads the insights that AdCrunch stores. It changes nothing and calls no provider. The sums start at the start date that applies, and they stop today, or at the end date that applies when it is in the past. asOf is the last day that the sums include: AdCrunch reads the insights once a day, and conversions keep arriving after the click. A Line Item with status `nothing_recorded` records no object: that is not a spend of 0. `no_data_yet` means that AdCrunch has no insight of its objects yet, and noDataYet names each object with no insight. advertiserNotOwned names each object in an ad account that this organization no longer owns: AdCrunch reads no insight of it. A campaign holds the spend of its ad sets, so an object with a recorded ancestor is not counted again. cpa, cpc, cpm and roas come from the sums. Money is in whole units of the currency of the Campaign Plan. AdCrunch never converts a currency: when an object spends in another currency, comparable is false and each money figure is null. pacing.ratio is spend divided by the spend that the budget expects by today. It is a ratio, not a verdict: decide with the user what is too low or too high. total reads the objects of all the Line Items over the window of the Campaign Plan, and counts each object once: when two Line Items record one object, or a campaign and an ad set inside it, the money counts once. So total can be less than the sum of the Line Items. total is not comparable when the Line Items do not share a unit, or when an object spends in another currency. 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 |
| --- | --- | --- | --- |
| `campaign_plan_name` | string | yes | The Campaign Plan slug — its stable per-organization handle. |

### Output

A successful call returns this object in `structuredContent`.

| Field | Type | Always present | Description |
| --- | --- | --- | --- |
| `campaignPlanId` | string | yes | The Campaign Plan, `cpl_…`. |
| `currency` | string | yes | The currency of the Campaign Plan, ISO 4217. |
| `lineItems` | array of object | yes | The delivery of each Line Item. |
| `lineItems[].advertiserNotOwned` | array of object | yes | The recorded objects in an ad account that this organization no longer owns. AdCrunch reads no insight of them. |
| `lineItems[].advertiserNotOwned[].entityId` | string | yes | The native provider id. |
| `lineItems[].advertiserNotOwned[].entityType` | string | yes | The provider word for the level: `campaign`, `adset`, `ad`. |
| `lineItems[].advertiserNotOwned[].provider` | one of `meta`, `gads`, `tiktok`, `x`, `openai`, `snapchat`, `dv360` | yes |  |
| `lineItems[].asOf` | string or null | yes | The most recent day that the sums include. AdCrunch reads the insights once a day, and conversions keep arriving after the click. |
| `lineItems[].budget` | number or null | yes | The budget of the Line Item, in whole units of the Campaign Plan currency. |
| `lineItems[].channel` | one of `meta`, `tiktok`, `snapchat`, `google_search`, `google_pmax`, `google_display`, `youtube`, `programmatic_display`, `linkedin`, `x` | yes |  |
| `lineItems[].counted` | array of object | yes | The recorded objects that the sums count: each one with no recorded ancestor. A campaign holds the spend of its ad sets, so an ad set recorded with its campaign is not counted twice. |
| `lineItems[].counted[].entityId` | string | yes | The native provider id. |
| `lineItems[].counted[].entityType` | string | yes | The provider word for the level: `campaign`, `adset`, `ad`. |
| `lineItems[].counted[].provider` | one of `meta`, `gads`, `tiktok`, `x`, `openai`, `snapchat`, `dv360` | yes |  |
| `lineItems[].delivered` | object or null | yes | The sums. `null` unless `status` is `delivered`. |
| `lineItems[].delivered.clicks` | number | yes | The sum of the clicks. |
| `lineItems[].delivered.comparable` | boolean | yes | Whether the money is in the currency of the Campaign Plan. `false` when an object spent in another currency: each money figure is then `null`. |
| `lineItems[].delivered.conversionValue` | number or null | yes | The sum of the conversion value, in whole units of the Campaign Plan currency. `null` when `comparable` is false: AdCrunch never converts a currency. |
| `lineItems[].delivered.conversions` | number | yes | The sum of the conversions. |
| `lineItems[].delivered.cpa` | number or null | yes | `spend / conversions`, from the sums, in whole units of the Campaign Plan currency. `null` when the divisor is 0, or when `comparable` is false. |
| `lineItems[].delivered.cpc` | number or null | yes | `spend / clicks`, from the sums, in whole units of the Campaign Plan currency. `null` when the divisor is 0, or when `comparable` is false. |
| `lineItems[].delivered.cpm` | number or null | yes | `spend × 1000 / impressions`, from the sums, in whole units of the Campaign Plan currency. `null` when the divisor is 0, or when `comparable` is false. |
| `lineItems[].delivered.currencies` | array of string or null | yes | The account currencies of the counted objects. `null` is a currency that AdCrunch does not know. |
| `lineItems[].delivered.impressions` | number | yes | The sum of the impressions. |
| `lineItems[].delivered.roas` | number or null | yes | `conversionValue / spend`, from the sums, with two decimals. It needs one account currency, not the currency of the Campaign Plan. `null` when the spend is 0. |
| `lineItems[].delivered.spend` | number or null | yes | The sum of the spend, in whole units of the Campaign Plan currency. `null` when `comparable` is false: AdCrunch never converts a currency. |
| `lineItems[].lineItemId` | string | yes | The Line Item, `lni_…`. |
| `lineItems[].noDataYet` | array of object | yes | The recorded objects that have no insight in the window yet. They are not a spend of 0. |
| `lineItems[].noDataYet[].entityId` | string | yes | The native provider id. |
| `lineItems[].noDataYet[].entityType` | string | yes | The provider word for the level: `campaign`, `adset`, `ad`. |
| `lineItems[].noDataYet[].provider` | one of `meta`, `gads`, `tiktok`, `x`, `openai`, `snapchat`, `dv360` | yes |  |
| `lineItems[].pacing` | object or null | yes | `null` with no budget, or with no start date that applies. |
| `lineItems[].pacing.elapsedDays` | number | yes | The days of the window up to today, today included. 0 before the start. |
| `lineItems[].pacing.expectedSpend` | number | yes | The spend that the budget expects by today, in whole units of the Campaign Plan currency. A `total` budget: budget × elapsed days / window days. A `daily` budget: budget × elapsed days. |
| `lineItems[].pacing.ratio` | number or null | yes | `spend / expectedSpend`, with two decimals. `null` when the expected spend is 0, or when the spend is not known or not comparable. A ratio, not a verdict: decide with the user what is too low or too high. |
| `lineItems[].pacing.windowDays` | number or null | yes | The days of the window of a `total` budget. `null` for a `daily` budget. |
| `lineItems[].since` | string or null | yes | The first day that the sums read: the start that applies. `null` reads from the first day. |
| `lineItems[].status` | one of `nothing_recorded`, `no_data_yet`, `delivered` | yes | `nothing_recorded`: the Line Item records no object, which is not a spend of 0. `no_data_yet`: it records objects, and AdCrunch has no insight of them in the window. `delivered`: the sums of the counted objects. |
| `lineItems[].target` | object or null | yes | `null` when the Line Item has no target. |
| `lineItems[].target.delivered` | number or null | yes | The delivered value of the metric, or `null` when it is not a number. |
| `lineItems[].target.direction` | one of `ceiling`, `floor` | yes | `ceiling`: a value at or below the target meets it (a cost). `floor`: a value at or above it meets it (a return). |
| `lineItems[].target.met` | boolean or null | yes | Whether the delivered value meets the target. `null` when the delivered value is `null`. |
| `lineItems[].target.metric` | one of `cpa`, `cpc`, `cpm`, `roas` | yes | The metric. |
| `lineItems[].target.value` | number | yes | The target: whole units of the Campaign Plan currency for `cpa`, `cpc` and `cpm`, a plain ratio for `roas`. |
| `lineItems[].unit` | one of `total`, `daily` | yes | How to read `budget`: `total` for the whole window, `daily` for each day. |
| `lineItems[].until` | string | yes | The last day that the sums read: today, or the end that applies when it is in the past. |
| `slug` | string | yes | The handle of the Campaign Plan. |
| `today` | string | yes | The day that the pacing counts to, UTC. |
| `total` | object or null | yes | `null` when no Line Item delivered in the window of the Campaign Plan. |
| `total.comparable` | `true` or `false` | yes | `false`: The Line Items do not share one unit, or an object spent in another currency than the Campaign Plan, so there is no total to state: say "not comparable". |
| `total.asOf` | string | yes | Only when `comparable` is `true`. The most recent day that the sums include. |
| `total.counted` | array of object | yes | Only when `comparable` is `true`. The objects that the total counts: across all the Line Items, each one once, and only when no other recorded object is its ancestor. |
| `total.counted[].entityId` | string | yes | The native provider id. |
| `total.counted[].entityType` | string | yes | The provider word for the level: `campaign`, `adset`, `ad`. |
| `total.counted[].provider` | one of `meta`, `gads`, `tiktok`, `x`, `openai`, `snapchat`, `dv360` | yes |  |
| `total.since` | string or null | yes | Only when `comparable` is `true`. The first day that the total reads: the start of the Campaign Plan. `null` reads from the first day. |
| `total.until` | string | yes | Only when `comparable` is `true`. The last day that the total reads: today, or the end of the Campaign Plan when it is in the past. |
| `total.delivered` | object | yes | Only when `comparable` is `true`. The sums of the counted objects. Each metric comes from the sums, never from a mean of the days. |
| `total.delivered.clicks` | number | yes | The sum of the clicks. |
| `total.delivered.comparable` | boolean | yes | Whether the money is in the currency of the Campaign Plan. `false` when an object spent in another currency: each money figure is then `null`. |
| `total.delivered.conversionValue` | number or null | yes | The sum of the conversion value, in whole units of the Campaign Plan currency. `null` when `comparable` is false: AdCrunch never converts a currency. |
| `total.delivered.conversions` | number | yes | The sum of the conversions. |
| `total.delivered.cpa` | number or null | yes | `spend / conversions`, from the sums, in whole units of the Campaign Plan currency. `null` when the divisor is 0, or when `comparable` is false. |
| `total.delivered.cpc` | number or null | yes | `spend / clicks`, from the sums, in whole units of the Campaign Plan currency. `null` when the divisor is 0, or when `comparable` is false. |
| `total.delivered.cpm` | number or null | yes | `spend × 1000 / impressions`, from the sums, in whole units of the Campaign Plan currency. `null` when the divisor is 0, or when `comparable` is false. |
| `total.delivered.currencies` | array of string or null | yes | The account currencies of the counted objects. `null` is a currency that AdCrunch does not know. |
| `total.delivered.impressions` | number | yes | The sum of the impressions. |
| `total.delivered.roas` | number or null | yes | `conversionValue / spend`, from the sums, with two decimals. It needs one account currency, not the currency of the Campaign Plan. `null` when the spend is 0. |
| `total.delivered.spend` | number or null | yes | The sum of the spend, in whole units of the Campaign Plan currency. `null` when `comparable` is false: AdCrunch never converts a currency. |
| `total.asOf` | string | yes | Only when `comparable` is `false`. The most recent day that the sums include. |
| `total.counted` | array of object | yes | Only when `comparable` is `false`. The objects that the total counts: across all the Line Items, each one once, and only when no other recorded object is its ancestor. |
| `total.counted[].entityId` | string | yes | The native provider id. |
| `total.counted[].entityType` | string | yes | The provider word for the level: `campaign`, `adset`, `ad`. |
| `total.counted[].provider` | one of `meta`, `gads`, `tiktok`, `x`, `openai`, `snapchat`, `dv360` | yes |  |
| `total.since` | string or null | yes | Only when `comparable` is `false`. The first day that the total reads: the start of the Campaign Plan. `null` reads from the first day. |
| `total.until` | string | yes | Only when `comparable` is `false`. The last day that the total reads: today, or the end of the Campaign Plan when it is in the past. |
| `total.delivered` | null | yes | Only when `comparable` is `false`. |

### 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`
- `organization_unentitled`
- `forbidden`
- `invalid_request`
- `internal_error`

### Scope

The token must hold `campaign_plan:read` and `observe:read`. [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.

- **Read-only.** The tool changes nothing.
- **Closed world.** The tool reads and writes the data of AdCrunch only.

### Example

The arguments:

```json
{
  "campaign_plan_name": "spring-sale"
}
```

The result, in `structuredContent`:

```json
{
  "campaignPlanId": "cpl_8f2k1m",
  "currency": "EUR",
  "lineItems": [
    {
      "advertiserNotOwned": [],
      "asOf": "2026-03-09",
      "budget": 3100,
      "channel": "meta",
      "counted": [
        {
          "entityId": "120210000000000",
          "entityType": "campaign",
          "provider": "meta"
        }
      ],
      "delivered": {
        "clicks": 1600,
        "comparable": true,
        "conversionValue": 2400,
        "conversions": 40,
        "cpa": 20,
        "cpc": 0.5,
        "cpm": 10,
        "currencies": [
          "EUR"
        ],
        "impressions": 80000,
        "roas": 3,
        "spend": 800
      },
      "lineItemId": "lni_2d9e77",
      "noDataYet": [],
      "pacing": {
        "elapsedDays": 10,
        "expectedSpend": 1000,
        "ratio": 0.8,
        "windowDays": 31
      },
      "since": "2026-03-01",
      "status": "delivered",
      "target": {
        "delivered": 20,
        "direction": "ceiling",
        "met": true,
        "metric": "cpa",
        "value": 25
      },
      "unit": "total",
      "until": "2026-03-10"
    }
  ],
  "slug": "spring-sale",
  "today": "2026-03-10",
  "total": {
    "asOf": "2026-03-09",
    "comparable": true,
    "counted": [
      {
        "entityId": "120210000000000",
        "entityType": "campaign",
        "provider": "meta"
      }
    ],
    "delivered": {
      "clicks": 1600,
      "comparable": true,
      "conversionValue": 2400,
      "conversions": 40,
      "cpa": 20,
      "cpc": 0.5,
      "cpm": 10,
      "currencies": [
        "EUR"
      ],
      "impressions": 80000,
      "roas": 3,
      "spend": 800
    },
    "since": "2026-03-01",
    "until": "2026-03-10"
  }
}
```
