> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adcontextprotocol.org/llms.txt
> Use this file to discover all available pages before exploring further.

# get_media_buy_delivery

> get_media_buy_delivery task — retrieve impressions, spend, pacing, and dimensional breakdowns for active AdCP campaigns. Supports custom date ranges and metric filtering.

Retrieve comprehensive delivery metrics and performance data for media buy reporting.

**Response Time**: \~60 seconds (reporting query)

## Scope

`get_media_buy_delivery` works on any `media_buy_id` returned by [`get_media_buys`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/get_media_buys), regardless of how the underlying campaign was created. Sales agents MUST NOT refuse delivery reporting — or narrow its coverage — on the basis that the buy originated outside AdCP. If delivery data for a buy is genuinely unavailable (e.g., the ad server has not yet reported a flight), the seller returns the buy in `media_buy_deliveries` with zero or partial metrics; the seller does not omit it and does not return [`MEDIA_BUY_NOT_FOUND`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-media-buy-not-found) for an account-owned buy.

**Request Schema**: [`/schemas/3.2.0-beta.7/media-buy/get-media-buy-delivery-request.json`](https://adcontextprotocol.org/schemas/3.2.0-beta.7/media-buy/get-media-buy-delivery-request.json)
**Response Schema**: [`/schemas/3.2.0-beta.7/media-buy/get-media-buy-delivery-response.json`](https://adcontextprotocol.org/schemas/3.2.0-beta.7/media-buy/get-media-buy-delivery-response.json)

## Request Parameters

| Parameter                  | Type                                                                                               | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| -------------------------- | -------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account`                  | [account-ref](/dist/docs/3.2.0-beta.7/building/by-layer/L2/accounts-and-agents#account-references) | No       | Account reference. Pass `{ "account_id": "..." }` or `{ "brand": {...}, "operator": "..." }` if the seller supports implicit resolution. Only returns media buys belonging to this account. When omitted, returns data across all accessible accounts.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `media_buy_ids`            | string\[]                                                                                          | No\*     | Array of media buy IDs to retrieve                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `status_filter`            | string \| string\[]                                                                                | No       | Status filter: `"pending_creatives"`, `"pending_start"`, `"active"`, `"paused"`, `"completed"`. Defaults to `["active"]` when omitted.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `start_date`               | string                                                                                             | No       | Report start date (YYYY-MM-DD), inclusive. Omit for campaign lifetime data. Only accepted when product supports `date_range`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `end_date`                 | string                                                                                             | No       | Report end date (YYYY-MM-DD), **exclusive**. Omit for campaign lifetime data. Only accepted when product supports `date_range`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `reporting_dimensions`     | object                                                                                             | No       | Request dimensional breakdowns within `by_package`. Include a key as an empty object (e.g., `"device_type": {}`) to activate with defaults. Keys: `geo`, `device_type`, `device_platform`, `format`, `audience`, `demographic`, `placement`, `spot`, `creative`, `keyword`, `catalog_item`. Most accept optional `limit`, `sort_by`, and `sort_direction`. `format` groups rows by canonical `format_kind`, not duration or other format-option parameters. Demographic also accepts canonical `age_ranges`, each of which must match the product's independent demographic reporting capability exactly. `spot` accepts only `limit`, returns rows by `aired_at` ascending, and defaults to the complete log for the reporting period. Geo requires `geo_level`; include `system` for metro/postal levels. `creative`, `keyword`, and `catalog_item` are returned automatically when the seller supports them, even when omitted from this object — including one of their keys is optional and upgrades that breakdown to a negotiated contract (`limit`, `sort_by`, `sort_direction`, and a binding truncation/sort-echo disclosure) without changing the automatic default. Unsupported dimensions are silently omitted; unsupported requested demographic ranges return [`UNSUPPORTED_FEATURE`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-unsupported-feature); malformed requests return a validation error. |
| `time_granularity`         | string                                                                                             | No       | Per-window slice granularity for pull recovery, matching `reporting_webhook.reporting_frequency` vocabulary (`hourly`, `daily`, `weekly`, `monthly`, `quarterly`, `post_campaign`). When set, the response includes `windows[]` slices shape-aligned with webhook fires at the same granularity. Capability-scoped — value MUST be in the product's `reporting_capabilities.windowed_pull_granularities`. See [Windowed pull recovery](#windowed-pull-recovery).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `include_window_breakdown` | boolean                                                                                            | No       | When `true` (and `time_granularity` is set), include the `windows[]` array on each media buy. Defaults to `false`. Ignored when `time_granularity` is omitted.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `requested_metrics`        | string\[]                                                                                          | No       | Narrow the response to a subset of metrics — same semantics as `reporting_webhook.requested_metrics`. Applies to `totals`, `by_package`, daily/window slices, and breakdown rows. When omitted, all available metrics are included (unchanged behavior). See [Requesting a metric subset](#requesting-a-metric-subset).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

> **Date Range Behavior**: The date range is **start-inclusive, end-exclusive**. For example, `start_date: "2026-01-01"` and `end_date: "2026-01-02"` returns delivery data for January 1st only (from `2026-01-01 00:00:00` up to, but not including, `2026-01-02 00:00:00`). To get a full week of data (Jan 1-7), use `end_date: "2026-01-08"`.

### Deterministic filter contract

`media_buy_ids` and accepted date bounds MUST observably scope the response.
Every `media_buy_deliveries[]` row MUST have a `media_buy_id` in
`media_buy_ids` when that filter is present. A seller MUST NOT accept the filter
and return delivery rows for other buys.

For an accepted `start_date` and `end_date`, `reporting_period.start` and
`reporting_period.end` MUST represent those exact UTC day boundaries, and all
aggregates, per-buy totals, daily rows, and requested window slices MUST be
computed only from delivery in the start-inclusive, end-exclusive interval.
Delivery outside that interval MUST NOT contribute to the response.

A filtered request can legitimately return the same rows and values as a less
restrictive request when all underlying delivery already matches. Conformance is
therefore established from returned membership, status, and date boundaries—not
by requiring two response payloads to differ.

**Date Range Examples**:

| start\_date  | end\_date    | Data Returned                    |
| ------------ | ------------ | -------------------------------- |
| `2026-01-01` | `2026-01-02` | January 1st only (1 day)         |
| `2026-01-01` | `2026-01-08` | January 1st through 7th (7 days) |
| `2026-01-01` | `2026-02-01` | Full month of January (31 days)  |
| `2026-01-15` | `2026-01-16` | January 15th only (1 day)        |

\*`media_buy_ids` filters results to specific media buys. If neither provided, returns all media buys in current session context.

## Response

Returns delivery report with aggregated totals and per-media-buy breakdowns:

| Field                  | Description                                                                                                                                                                                                                                                              |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `reporting_period`     | Date range for report (start/end timestamps)                                                                                                                                                                                                                             |
| `currency`             | ISO 4217 currency code (USD, EUR, GBP, etc.)                                                                                                                                                                                                                             |
| `attribution_window`   | Attribution methodology: `post_click` and `post_view` (duration objects), and `model` (last\_touch, first\_touch, linear, time\_decay, data\_driven)                                                                                                                     |
| `aggregated_totals`    | Combined metrics across all media buys (impressions, spend, clicks, views, completed\_views, conversions, conversion\_value, roas, new\_to\_brand\_rate, cost\_per\_acquisition, completion\_rate, reach, reach\_unit, frequency, media\_buy\_count, metric\_aggregates) |
| `media_buy_deliveries` | Array of delivery data per media buy                                                                                                                                                                                                                                     |

### Media Buy Delivery Object

<Note>
  **3.1 vocabulary note.** `get_media_buy_delivery` returns the lifecycle state on a nested `media_buy_deliveries[].status` field (no envelope collision — nested at depth 1). [`create_media_buy`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/create_media_buy) and [`update_media_buy`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/update_media_buy) success responses return the same lifecycle state on a top-level **`media_buy_status`** field (added in 3.1 to avoid colliding with the envelope task-status `status`). Same enum, two field names in 3.1 — the cascade unifies in 4.0 ([#4905](https://github.com/adcontextprotocol/adcp/issues/4905)). See [Migration › `media_buy_status`](/dist/docs/3.2.0-beta.7/reference/migration/media-buy-status) for the full picture.
</Note>

| Field             | Description                                                                                                                                                                                                                                                                   |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `media_buy_id`    | Media buy identifier                                                                                                                                                                                                                                                          |
| `status`          | Current status (`pending_creatives`, `pending_start`, `active`, `paused`, `completed`). In webhook context, may also be `reporting_delayed` or `failed`. Maps to `media_buy_status` on `create_media_buy` / `update_media_buy` success responses (3.1 vocabulary note above). |
| `totals`          | Aggregate metrics (impressions, spend, clicks, ctr, conversions, conversion\_value, roas, new\_to\_brand\_rate)                                                                                                                                                               |
| `by_package`      | Package-level breakdowns with delivery\_status, paused state, and pacing\_index                                                                                                                                                                                               |
| `daily_breakdown` | Day-by-day delivery (date, impressions, spend, conversions, conversion\_value, roas, new\_to\_brand\_rate)                                                                                                                                                                    |

See [schema](https://adcontextprotocol.org/schemas/3.2.0-beta.7/media-buy/get-media-buy-delivery-response.json) for complete field list.

### Final vs provisional numbers

A delivery row is either **final for its measurement window** or it isn't. Final means the seller considers these numbers closed for the period — no further revision — and is willing to invoice on them, subject to whatever `measurement_terms.billing_measurement` the buy was created with. Anything else is provisional: still settling as measurement matures (broadcast C3 → C7 DVR accumulation, post-IVT scrubbing, conversion dedup) and **not** an invoicing source of truth.

Per-row signals:

* `media_buy_deliveries[*].is_final` and `media_buy_deliveries[*].finalized_at` — row-level finality, true iff every package in the row is final for the same measurement window.
* `media_buy_deliveries[*].by_package[*].is_final` and `.finalized_at` — package-level finality with the exact timestamp.
* `media_buy_deliveries[*].by_package[*].measurement_window` — which maturation stage the numbers represent (`c3`, `c7`, `post_sivt`, `downloads_30d`, …).

Callers using rows where `is_final` is false (or absent) for pacing and reporting are safe; callers using them for reconciliation, accruals, or finance close are not.

### Who is authoritative for billing

Whose number invoices the buy is a **contract term**, declared in the buy's [`measurement_terms.billing_measurement`](/dist/docs/3.2.0-beta.7/media-buy/advanced-topics/billing-authority):

* **Seller-attested** (the default when `billing_measurement` is absent or names the seller's own ad server): invoice off final rows on `get_media_buy_delivery`.
* **Vendor-attested** (third-party measurement vendor named in the buy — e.g., Nielsen, IAS, DV, MOAT): invoice off the named vendor's authoritative numbers. Operationally this is most often the seller pulling from the vendor and publishing on `get_media_buy_delivery` with `is_final: true`; when the buyer holds the vendor relationship instead, the buyer pushes via [`report_usage`](/dist/docs/3.2.0-beta.7/accounts/tasks/report_usage) with `final: true` and `finalized_at` set.
* **Buyer-attested** (buyer's 3PAS or MMP named in the buy — e.g., CM360, Flashtalking): invoice off the buyer's final records pushed via `report_usage`.

When the authoritative party doesn't publish final numbers within `measurement_terms.billing_measurement.finalization_deadline_hours`, the counterparty MAY fall back to its own attestation; the breach is handled under `makegood_policy`. The deadline applies symmetrically to whichever party is named in `vendor`. When variance between parties exceeds `max_variance_percent`, parties resolve via the buy's [`makegood_policy.available_remedies`](/dist/docs/3.2.0-beta.7/media-buy/advanced-topics/accountability) and out-of-band negotiation.

See [Billing authority](/dist/docs/3.2.0-beta.7/media-buy/advanced-topics/billing-authority) for the end-to-end flow. A structured dispute task — opening, transitioning, and resolving a delivery dispute on the wire — is targeted for AdCP 3.2.

### Aggregated metric partitions (`metric_aggregates`)

Cross-buy delivery values that vary by **qualifier** (measurement standard, transparency disclosure) are reported as a partitioned array on `aggregated_totals.metric_aggregates` rather than as flat scalars. This solves the apples-to-oranges sum problem at the aggregate layer: MRC and GroupM viewability define materially different thresholds and must never be combined into a single rate.

Each row carries the same atomic unit as `package.committed_metrics` and `by_package[].missing_metrics`:

```
committed_metrics row : { scope, metric_id, qualifier, committed_at }
missing_metrics row   : { scope, metric_id, qualifier }
metric_aggregates row : { scope, metric_id, qualifier, value, ...components }
```

**Reconciliation is a row-level join on `(scope, metric_id, qualifier)`.** For each `committed_metrics` row, find the matching `metric_aggregates` row; absent matches surface as `missing_metrics`. No per-metric reconciliation logic, no traversal asymmetry between contract and delivery.

**Granularity rule.** One row per `(metric_id, full-qualifier-set)`, reported at the finest available granularity. Buyers re-aggregate up if they want a coarser view. This eliminates rollup ambiguity and prevents accidental double-counting.

**Unqualified metrics stay top-level.** `impressions`, `spend`, `media_buy_count`, and other metrics with no qualifier remain at the top of `aggregated_totals`. `metric_aggregates` is only used for metrics with non-empty qualifier sets.

**Mutual exclusion (MUST).** For any `metric_id` appearing in `metric_aggregates`, the corresponding top-level scalar in `aggregated_totals` MUST be omitted — not zeroed. Sellers MUST NOT emit both. Avoids duplicate sources of truth.

**Qualifier vocabulary** is closed today on both `committed_metrics` and `metric_aggregates` (`additionalProperties: false`). Five keys exist: `viewability_standard` (MRC vs GroupM viewability), `completion_source` (seller- vs vendor-attested completion), `attribution_methodology` (deterministic\_purchase / probabilistic / panel\_based / modeled — for outcome metrics), `attribution_window` (structured duration — for outcome metrics), and `lift_dimension` (awareness / consideration / favorability / purchase\_intent / ad\_recall — for `brand_lift`). The delivery vocabulary is **expected to diverge from contract** in future minors as transparency disclosures buyers don't commit to ship delivery-only (e.g., `tracker_firing` pending #3832). New qualifier keys ship explicitly in subsequent minors on either surface. **Heterogeneous value types**: qualifier values are mostly string enums but `attribution_window` is an object-valued duration; consumers MUST dispatch on key name to know value shape, and structured-value qualifiers join on canonical (key-sorted) deep equality.

**Qualifier-set drift across reports.** When a campaign gains a new qualifier mid-flight (e.g., adds `tracker_firing` partitioning in week 2 after only client-side firing in week 1), prior periods' rows remain valid at their original granularity. Buyers SHOULD NOT retroactively repartition; report supersession via `supersedes_window` is the documented path for window-level revisions.

**Per-buy `totals` shape stays flat.** Each individual buy is single-qualifier by definition; only the cross-buy aggregate spans qualifiers and needs the partitioned shape. Per-buy `totals.viewability` continues to be a flat object with its own `standard` field.

Example:

```json theme={null}
{
  "aggregated_totals": {
    "impressions": 1000000,
    "spend": 5000.00,
    "media_buy_count": 3,
    "metric_aggregates": [
      {
        "scope": "standard",
        "metric_id": "viewable_rate",
        "qualifier": { "viewability_standard": "mrc" },
        "value": 0.7286,
        "measurable_impressions": 700000,
        "viewable_impressions": 510000
      },
      {
        "scope": "standard",
        "metric_id": "viewable_rate",
        "qualifier": { "viewability_standard": "groupm" },
        "value": 0.55,
        "measurable_impressions": 180000,
        "viewable_impressions": 99000
      }
    ]
  }
}
```

**Value typing dispatch.** Buyer agents MUST inspect `metric_id` before doing arithmetic. Rate metrics (`viewable_rate`, `completion_rate`) are 0.0–1.0; cost-per metrics are currency; count metrics are non-negative numbers; ROAS is a ratio. Same dispatch convention as `committed_metrics` — `metric_id` is the type tag.

## Common Scenarios

### Single Media Buy

<CodeGroup>
  ```javascript JavaScript test=false theme={null}
  import { testAgent } from '@adcp/sdk/testing';
  import { GetMediaBuyDeliveryResponseSchema } from '@adcp/sdk';

  // Get single media buy delivery report
  const result = await testAgent.getMediaBuyDelivery({
    media_buy_ids: ['mb_12345'],
    start_date: '2024-02-01',
    end_date: '2024-02-07'
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data);

  // Check for errors (discriminated union response)
  if ('errors' in validated && validated.errors) {
    throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`);
  }

  console.log(`Delivered ${validated.aggregated_totals.impressions.toLocaleString()} impressions`);
  console.log(`Spend: $${validated.aggregated_totals.spend.toFixed(2)}`);
  if (validated.media_buy_deliveries.length > 0) {
    console.log(`CTR: ${(validated.media_buy_deliveries[0].totals.ctr * 100).toFixed(2)}%`);
  }
  ```

  ```python Python integration=true theme={null}
  import asyncio
  from adcp.testing import test_agent
  from adcp.types import GetMediaBuyDeliveryRequest

  async def main():
      # Get single media buy delivery report
      result = await test_agent.get_media_buy_delivery(
          GetMediaBuyDeliveryRequest(
              media_buy_ids=['mb_12345'],
              start_date='2024-02-01',
              end_date='2024-02-07'
          )
      )

      # Check for errors (discriminated union response)
      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Query failed: {result.errors}")

      print(f"Delivered {result.aggregated_totals.impressions:,} impressions")
      print(f"Spend: ${result.aggregated_totals.spend:.2f}")
      if result.media_buy_deliveries:
          print(f"CTR: {result.media_buy_deliveries[0].totals.ctr * 100:.2f}%")

  asyncio.run(main())
  ```
</CodeGroup>

### Multiple Media Buys

<CodeGroup>
  ```javascript JavaScript test=false theme={null}
  import { testAgent } from '@adcp/sdk/testing';
  import { GetMediaBuyDeliveryResponseSchema } from '@adcp/sdk';

  // Get all active media buys from context
  const result = await testAgent.getMediaBuyDelivery({
    status_filter: 'active',
    start_date: '2024-02-01',
    end_date: '2024-02-07'
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data);

  if ('errors' in validated && validated.errors) {
    throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`);
  }

  console.log(`${validated.aggregated_totals.media_buy_count} active campaigns`);
  console.log(`Total impressions: ${validated.aggregated_totals.impressions.toLocaleString()}`);
  console.log(`Total spend: $${validated.aggregated_totals.spend.toFixed(2)}`);

  // Review each campaign
  validated.media_buy_deliveries.forEach(delivery => {
    console.log(`${delivery.media_buy_id}: ${delivery.totals.impressions.toLocaleString()} impressions, CTR ${(delivery.totals.ctr * 100).toFixed(2)}%`);
  });
  ```

  ```python Python test=false theme={null}
  import asyncio
  from adcp.testing import test_agent
  from adcp.types import GetMediaBuyDeliveryRequest

  async def main():
      # Get all active media buys from context
      result = await test_agent.get_media_buy_delivery(
          GetMediaBuyDeliveryRequest(
              status_filter='active',
              start_date='2024-02-01',
              end_date='2024-02-07'
          )
      )

      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Query failed: {result.errors}")

      print(f"{result.aggregated_totals.media_buy_count} active campaigns")
      print(f"Total impressions: {result.aggregated_totals.impressions:,}")
      print(f"Total spend: ${result.aggregated_totals.spend:.2f}")

      # Review each campaign
      for delivery in result.media_buy_deliveries:
          print(f"{delivery.media_buy_id}: {delivery.totals.impressions:,} impressions, CTR {delivery.totals.ctr * 100:.2f}%")

  asyncio.run(main())
  ```
</CodeGroup>

### Date Range Reporting

<CodeGroup>
  ```javascript JavaScript test=false theme={null}
  import { testAgent } from '@adcp/sdk/testing';
  import { GetMediaBuyDeliveryResponseSchema } from '@adcp/sdk';

  // Get month-to-date performance
  const now = new Date();
  const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
  const dateFormat = date => date.toISOString().split('T')[0];

  const result = await testAgent.getMediaBuyDelivery({
    media_buy_ids: ['mb_12345'],
    start_date: dateFormat(monthStart),
    end_date: dateFormat(now)
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data);

  if ('errors' in validated && validated.errors) {
    throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`);
  }

  if (validated.media_buy_deliveries.length > 0) {
    // Analyze daily breakdown
    const dailyBreakdown = validated.media_buy_deliveries[0].daily_breakdown;
    if (dailyBreakdown && dailyBreakdown.length > 0) {
      console.log(`Daily average: ${Math.round(validated.aggregated_totals.impressions / dailyBreakdown.length).toLocaleString()} impressions`);

      // Find peak day
      const peakDay = dailyBreakdown.reduce((max, day) =>
        day.impressions > max.impressions ? day : max
      );
      console.log(`Peak day: ${peakDay.date} with ${peakDay.impressions.toLocaleString()} impressions`);
    }
  }
  ```

  ```python Python test=false theme={null}
  import asyncio
  from adcp.testing import test_agent
  from adcp.types import GetMediaBuyDeliveryRequest
  from datetime import date

  async def main():
      # Get month-to-date performance
      today = date.today()
      month_start = date(today.year, today.month, 1)

      result = await test_agent.get_media_buy_delivery(
          GetMediaBuyDeliveryRequest(
              media_buy_ids=['mb_12345'],
              start_date=str(month_start),
              end_date=str(today)
          )
      )

      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Query failed: {result.errors}")

      if result.media_buy_deliveries:
          # Analyze daily breakdown
          daily_breakdown = result.media_buy_deliveries[0].daily_breakdown
          if daily_breakdown:
              daily_avg = result.aggregated_totals.impressions // len(daily_breakdown)
              print(f"Daily average: {daily_avg:,} impressions")

              # Find peak day
              peak_day = max(daily_breakdown, key=lambda d: d.impressions)
              print(f"Peak day: {peak_day.date} with {peak_day.impressions:,} impressions")

  asyncio.run(main())
  ```
</CodeGroup>

### Multi-Status Query

<CodeGroup>
  ```javascript JavaScript integration=true theme={null}
  import { testAgent } from '@adcp/sdk/testing';
  import { GetMediaBuyDeliveryResponseSchema } from '@adcp/sdk';

  // Get both active and paused campaigns
  const result = await testAgent.getMediaBuyDelivery({
    status_filter: ['active', 'paused'],
    start_date: '2024-02-01',
    end_date: '2024-02-07'
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data);

  if ('errors' in validated && validated.errors) {
    throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`);
  }

  // Group by status
  const byStatus = validated.media_buy_deliveries.reduce((acc, delivery) => {
    if (!acc[delivery.status]) acc[delivery.status] = [];
    acc[delivery.status].push(delivery);
    return acc;
  }, {});

  console.log(`Active campaigns: ${byStatus.active?.length || 0}`);
  console.log(`Paused campaigns: ${byStatus.paused?.length || 0}`);

  // Identify underperforming campaigns
  byStatus.paused?.forEach(delivery => {
    if (delivery.by_package && delivery.by_package.length > 0) {
      const avgPacing = delivery.by_package.reduce((sum, pkg) => sum + pkg.pacing_index, 0) / delivery.by_package.length;
      console.log(`${delivery.media_buy_id}: paused with ${(avgPacing * 100).toFixed(0)}% pacing`);
    }
  });
  ```

  ```python Python integration=true theme={null}
  import asyncio
  from adcp.testing import test_agent
  from adcp.types import GetMediaBuyDeliveryRequest
  from collections import defaultdict

  async def main():
      # Get both active and paused campaigns
      result = await test_agent.get_media_buy_delivery(
          GetMediaBuyDeliveryRequest(
              status_filter=['active', 'paused'],
              start_date='2024-02-01',
              end_date='2024-02-07'
          )
      )

      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Query failed: {result.errors}")

      # Group by status
      by_status = defaultdict(list)
      for delivery in result.media_buy_deliveries:
          by_status[delivery.status].append(delivery)

      print(f"Active campaigns: {len(by_status['active'])}")
      print(f"Paused campaigns: {len(by_status['paused'])}")

      # Identify underperforming campaigns
      for delivery in by_status['paused']:
          if delivery.by_package:
              avg_pacing = sum(pkg.pacing_index for pkg in delivery.by_package) / len(delivery.by_package)
              print(f"{delivery.media_buy_id}: paused with {avg_pacing * 100:.0f}% pacing")

  asyncio.run(main())
  ```
</CodeGroup>

### Correlating Your Own Reference

<Note>
  **`media_buy_ids` are seller-assigned.** They are the `media_buy_id` values returned by [`create_media_buy`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/create_media_buy) and [`get_media_buys`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/get_media_buys) — not buyer-side identifiers. AdCP has no buyer-reference lookup key: the top-level `buyer_ref` field was removed in 3.0.0, making seller-assigned `media_buy_id`/`package_id` canonical. If you hold only your own identifier, include it in the opaque `context` object (commonly `context.buyer_ref`) when you call `create_media_buy`; the seller echoes `context` back on `create_media_buy` and [`get_media_buys`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/get_media_buys), so you can reconcile your reference to the seller's `media_buy_id` before requesting delivery.
</Note>

<CodeGroup>
  ```javascript JavaScript integration=true theme={null}
  import { testAgent } from '@adcp/sdk/testing';
  import { GetMediaBuyDeliveryResponseSchema } from '@adcp/sdk';

  // Compare several media buys by their seller-assigned IDs (from create_media_buy / get_media_buys)
  const result = await testAgent.getMediaBuyDelivery({
    media_buy_ids: ['mb_q1_campaign_2024', 'mb_q1_retargeting_2024']
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data);

  if ('errors' in validated && validated.errors) {
    throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`);
  }

  // Lifetime delivery data (no date range specified)
  console.log(`Total lifetime impressions: ${validated.aggregated_totals.impressions.toLocaleString()}`);
  console.log(`Total lifetime spend: $${validated.aggregated_totals.spend.toFixed(2)}`);

  // Compare campaigns
  validated.media_buy_deliveries.forEach(delivery => {
    if (delivery.totals.impressions > 0) {
      const cpm = (delivery.totals.spend / delivery.totals.impressions) * 1000;
      console.log(`${delivery.media_buy_id}: CPM $${cpm.toFixed(2)}, CTR ${(delivery.totals.ctr * 100).toFixed(2)}%`);
    }
  });
  ```

  ```python Python integration=true theme={null}
  import asyncio
  from adcp.testing import test_agent
  from adcp.types import GetMediaBuyDeliveryRequest

  async def main():
      # Compare several media buys by their seller-assigned IDs (from create_media_buy / get_media_buys)
      result = await test_agent.get_media_buy_delivery(
          GetMediaBuyDeliveryRequest(
              media_buy_ids=['mb_q1_campaign_2024', 'mb_q1_retargeting_2024']
          )
      )

      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Query failed: {result.errors}")

      # Lifetime delivery data (no date range specified)
      print(f"Total lifetime impressions: {result.aggregated_totals.impressions:,}")
      print(f"Total lifetime spend: ${result.aggregated_totals.spend:.2f}")

      # Compare campaigns
      for delivery in result.media_buy_deliveries:
          if delivery.totals.impressions > 0:
              cpm = (delivery.totals.spend / delivery.totals.impressions) * 1000
              print(f"{delivery.media_buy_id}: CPM ${cpm:.2f}, CTR {delivery.totals.ctr * 100:.2f}%")

  asyncio.run(main())
  ```
</CodeGroup>

### Account-Scoped Query

<CodeGroup>
  ```javascript JavaScript test=false theme={null}
  import { testAgent } from '@adcp/sdk/testing';
  import { GetMediaBuyDeliveryResponseSchema } from '@adcp/sdk';

  // Get delivery for a specific advertiser account
  const result = await testAgent.getMediaBuyDelivery({
    account: { account_id: 'acc_acme_pinnacle' },
    status_filter: 'active',
    start_date: '2024-02-01',
    end_date: '2024-02-07'
  });

  if (!result.success) {
    throw new Error(`Request failed: ${result.error}`);
  }

  const validated = GetMediaBuyDeliveryResponseSchema.parse(result.data);

  if ('errors' in validated && validated.errors) {
    throw new Error(`Query failed: ${JSON.stringify(validated.errors)}`);
  }

  console.log(`${validated.aggregated_totals.media_buy_count} campaigns for account`);
  console.log(`Total spend: $${validated.aggregated_totals.spend.toFixed(2)}`);
  ```

  ```python Python test=false theme={null}
  import asyncio
  from adcp.testing import test_agent
  from adcp.types import GetMediaBuyDeliveryRequest

  async def main():
      # Get delivery for a specific advertiser account
      result = await test_agent.get_media_buy_delivery(
          GetMediaBuyDeliveryRequest(
              account={'account_id': 'acc_acme_pinnacle'},
              status_filter='active',
              start_date='2024-02-01',
              end_date='2024-02-07'
          )
      )

      if hasattr(result, 'errors') and result.errors:
          raise Exception(f"Query failed: {result.errors}")

      print(f"{result.aggregated_totals.media_buy_count} campaigns for account")
      print(f"Total spend: ${result.aggregated_totals.spend:.2f}")

  asyncio.run(main())
  ```
</CodeGroup>

## Metrics Definitions

| Metric                   | Definition                                                                                                                                                                                                                                                                                                                                      |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Impressions**          | Number of times ads were displayed                                                                                                                                                                                                                                                                                                              |
| **Spend**                | Amount spent in specified currency                                                                                                                                                                                                                                                                                                              |
| **Clicks**               | Number of ad clicks (if available)                                                                                                                                                                                                                                                                                                              |
| **CTR**                  | Click-through rate (clicks/impressions)                                                                                                                                                                                                                                                                                                         |
| **Views**                | Content engagements at the billable view threshold — video views, audio/podcast stream starts, or format-specific view events                                                                                                                                                                                                                   |
| **Completed Views**      | Audio/video completions (at threshold or 100%)                                                                                                                                                                                                                                                                                                  |
| **Completion Rate**      | Completion rate (completed\_views/impressions)                                                                                                                                                                                                                                                                                                  |
| **Conversions**          | Attributed conversions (purchases, new listeners, app installs, etc.)                                                                                                                                                                                                                                                                           |
| **Conversion Value**     | Total monetary value of attributed conversions                                                                                                                                                                                                                                                                                                  |
| **ROAS**                 | Return on ad spend (conversion\_value / spend)                                                                                                                                                                                                                                                                                                  |
| **New-to-Brand Rate**    | Fraction of conversions from first-time brand buyers (0-1)                                                                                                                                                                                                                                                                                      |
| **Cost per Acquisition** | Cost per conversion (spend / conversions)                                                                                                                                                                                                                                                                                                       |
| **Reach**                | Unique users reached (see `reach_unit` for measurement unit: individuals, households, devices, accounts, cookies). Measurement window declared via `reach_window`; without it, buyers must not sum reach across rows.                                                                                                                           |
| **Reach Unit**           | Unit of measurement for reach — required when reach is present                                                                                                                                                                                                                                                                                  |
| **Reach Window**         | Window semantics for reported reach/frequency: `cumulative` (uniques since campaign start), `period` (uniques within a single non-overlapping reporting period — e.g., daily snapshot), or `rolling` (uniques within a trailing window — e.g., trailing-7-day). Never sum across rows. Optional but strongly recommended when reach is present. |
| **Frequency**            | Average ad exposures per reach unit, measured over `reach_window`                                                                                                                                                                                                                                                                               |
| **Viewability**          | Object with `vendor`, `measurable_impressions` (denominator), `viewable_impressions`, `viewable_rate`, `viewed_seconds` (average in-view duration per measurable impression — pairs with the `viewed_seconds` optimization goal), and `standard`                                                                                                |
| **Follows**              | New followers, page likes, or free channel/feed subscribes attributed to delivery                                                                                                                                                                                                                                                               |
| **Pacing Index**         | Actual vs. expected delivery rate (1.0 = on track, \<1.0 = behind, >1.0 = ahead)                                                                                                                                                                                                                                                                |
| **CPM**                  | Cost per thousand impressions (spend/impressions \* 1000)                                                                                                                                                                                                                                                                                       |

## Query Behavior

### Context-Based Queries

* If no `media_buy_ids` provided, returns all media buys from current session context
* Context established by previous operations (e.g., [`create_media_buy`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/create_media_buy))

### Status Filtering

* Defaults to `["active"]` if not specified
* Can be single string (`"active"`) or array (`["active", "paused"]`)
* Valid filter values are media-buy lifecycle statuses: `pending_creatives`, `pending_start`, `active`, `paused`, `completed`
* `reporting_delayed` and `failed` are delivery/reporting statuses returned in webhook contexts, not request filter values
* Some legacy integrations may emit `pending`; treat it as equivalent to `pending_start`

### Date Ranges

* If dates not specified, returns campaign lifetime delivery data
* Both `start_date` and `end_date` must be provided together — partial date ranges are invalid
* Date format: `YYYY-MM-DD`
* **Start-inclusive, end-exclusive**: `start_date` is included, `end_date` is excluded. For example, `start_date: "2026-01-01"` and `end_date: "2026-01-02"` returns data for January 1st only.
* Products declare date range support in `reporting_capabilities.date_range_support`
* Products with `date_range_support: "lifetime_only"` reject requests that include `start_date`/`end_date` with an [`UNSUPPORTED_FEATURE`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-unsupported-feature) error
* Products with `date_range_support: "date_range"` accept date parameters and filter delivery data accordingly
* Daily breakdown may be truncated for long date ranges to reduce response size

### Metric Availability

* **Universal**: Impressions, spend (available on all platforms)
* **Format-dependent**: Clicks, completed\_views, completion\_rate (depends on inventory type and platform capabilities)
* **Audience**: Reach, frequency (available on platforms with deduplicated measurement)
* **Commerce attribution**: Conversions, conversion\_value, roas, new\_to\_brand\_rate (available on commerce media and streaming platforms)
* **Engagement**: Follows, saves, engagements, profile\_visits (available on social and streaming platforms)
* **Attribution window**: `attribution_window` describes the lookback windows and model used for conversion attribution (e.g., 14-day click, 1-day view, last\_touch)
* **Package-level**: All metrics broken down by package with pacing\_index

### Requesting a metric subset

`requested_metrics` narrows the response to a specific list of metrics — the same field and semantics as `reporting_webhook.requested_metrics`, now also available on the GET path. It applies to every metrics-bearing object in the response: `totals`, `by_package`, daily and window slices, and breakdown rows. `impressions` and `spend` are always included regardless of what the list contains. Requesting a leaf metric identity (e.g. `viewable_rate`, `quartile_75`) returns its canonical nested carrier (`viewability`, `quartile_data`) rather than a flat duplicate field. A metric requested but not available for the buy is simply omitted — no error — and `missing_metrics` is unaffected by that omission: it still reconciles solely against `committed_metrics`, and sellers MUST NOT list a metric there just because the buyer excluded it via `requested_metrics`. Values outside the product's `reporting_capabilities.available_metrics` are ignored. Omitting the field preserves current behavior — all available metrics are returned.

## Data Freshness

* Reporting data typically has 2-4 hour delay
* Real-time impression counts not available
* Use for periodic reporting and optimization decisions, not live monitoring

**Phased-maturation channels**: Data freshness differs for channels where billing-grade data is produced in phases rather than arriving final on day one — broadcast TV (Live → C3 → C7 DVR accumulation, final C7 \~15–22 days after broadcast), DOOH (tentative plays → post-IVT/fraud-check final), digital with IVT filtering (raw → post-GIVT → post-SIVT), and podcast (7-day → 30-day downloads). Products with `reporting_capabilities.measurement_windows` declare these timelines. Buyers reconcile against the `measurement_window` specified in `billing_measurement` on the agreed terms. See [Accountability](/dist/docs/3.2.0-beta.7/media-buy/advanced-topics/accountability) for measurement terms and [Optimization and reporting](/dist/docs/3.2.0-beta.7/media-buy/media-buys/optimization-reporting) for the full lifecycle.

## Error Handling

| Error Code                                                                                                                       | Description                                                                        | Resolution                                                                                                                                                                                               |
| -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`AUTH_MISSING`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-auth-missing)                       | No credentials presented                                                           | Provide credentials via auth header                                                                                                                                                                      |
| [`AUTH_INVALID`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-auth-invalid)                       | Credentials rejected (expired / revoked)                                           | Human credential rotation required; do not auto-retry                                                                                                                                                    |
| [`MEDIA_BUY_NOT_FOUND`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-media-buy-not-found)         | Media buy doesn't exist                                                            | Verify `media_buy_id`; for legacy correlation use [`get_media_buys`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/get_media_buys) + `context.internal_campaign_id`                                   |
| [`INVALID_REQUEST`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-invalid-request)                 | A date string or filter violates its schema constraints                            | Use YYYY-MM-DD dates and a documented status value                                                                                                                                                       |
| [`VALIDATION_ERROR`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-validation-error)               | Only one date is present, or `end_date` is not after `start_date`                  | Supply both dates with `start_date < end_date`, or omit both for lifetime delivery                                                                                                                       |
| [`UNSUPPORTED_FEATURE`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-unsupported-feature)         | Product only supports lifetime reporting                                           | Omit `start_date` and `end_date`. Check `reporting_capabilities.date_range_support` on the product.                                                                                                      |
| [`UNSUPPORTED_GRANULARITY`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-unsupported-granularity) | Requested `time_granularity` is not in the product's `windowed_pull_granularities` | Re-issue with a granularity from `error.details.supported_granularities`, or omit `time_granularity` to fall back to cumulative date-range pulls. See [Windowed pull recovery](#windowed-pull-recovery). |

A valid query with no matching media buys succeeds with an empty `media_buy_deliveries` array.

## Package-Level Metrics

The `by_package` array provides per-package delivery details with these key fields:

**Buyer Control**:

* **`paused`**: Whether the package is currently paused by the buyer (true/false)

**System State**:

* **`delivery_status`**: System-reported operational state:
  * `delivering` - Package is actively delivering impressions
  * `not_delivering` - Package recorded zero impressions for the entire reporting window while it was in-flight. Sellers should only report this for a final window; a provisional zero may reflect measurement lag.
  * `completed` - Package finished successfully
  * `budget_exhausted` - Package ran out of budget
  * `flight_ended` - Package reached its end date
  * `goal_met` - Package achieved its impression/conversion goal

**Performance**:

* **`pacing_index`**: Delivery pace (1.0 = on track, below 1.0 = behind, above 1.0 = ahead)
* **`rate`**: Effective pricing rate (e.g., CPM)
* **`pricing_model`**: How the package is billed (cpm, cpcv, cpp, revenue\_share, etc.)
* **`commissionable_value`**: For `revenue_share`, the settled attributed value to which the selected `commission_rate` applies. Verify `spend = round_currency(commissionable_value × commission_rate)`.

**Accountability**:

* **`missing_metrics`**: Metrics the binding reporting contract advertised but that are not populated in this report. Each entry uses an explicit `scope` discriminator: `{ "scope": "standard", "metric_id": "completed_views" }` for entries from the closed `available-metric.json` enum, `{ "scope": "vendor", "vendor": { "domain": "..." }, "metric_id": "attention_units" }` for vendor-defined metrics. Standard entries MAY carry a `qualifier` mirroring the `committed_metrics` qualifier (e.g., `{ "scope": "standard", "metric_id": "viewable_rate", "qualifier": { "viewability_standard": "mrc" } }` flags a missing MRC commitment even when GroupM viewability was reported, or `{ "scope": "standard", "metric_id": "completion_rate", "qualifier": { "completion_source": "vendor_attested" } }` flags a missing vendor-attested commitment even when seller-attested completion was reported — the paths are not interchangeable). Reconciled against `package.committed_metrics` (filtered to entries where `committed_at < reporting_period.end`) when present; falls back to the product's current `reporting_capabilities.available_metrics` and `vendor_metrics` when absent. Empty array (or absent) indicates clean delivery against the contract; non-empty signals an accountability breach. Sellers MUST exclude metrics that are not yet measurable for the current `measurement_window` (e.g., post-IVT counts during the live window) — those will appear (or not) when a wider window supersedes this report via `supersedes_window`.
* **`vendor_metric_values`**: Reported values for vendor-defined metrics that the product's `reporting_capabilities.vendor_metrics` declared (proprietary attention, emissions, panel demographics, brand-lift surveys, etc.). Each entry carries `{ vendor, metric_id, value, unit?, measurable_impressions?, breakdown? }`. The `measurable_impressions` field is the coverage denominator — vendor measurement is rarely 100% of delivery, since vendors only score impressions where their SDK fires or their panel matches. Buyers compute coverage as `measurable_impressions / impressions`. When `measurable_impressions` is absent, coverage is unspecified — buyers MUST NOT compute a coverage rate or assume full coverage. When a declared vendor metric is omitted entirely from this array, infer no measurement happened (no integration). JIC or panel-based co-viewing adjustments, claim reconciliation, confidence intervals, and panel sizes belong here, usually in `breakdown`, rather than in buy-time signal targeting definitions.

**Key Distinction**: `paused` reflects buyer control, while `delivery_status` reflects system reality. A package can be not paused but have `delivery_status: "budget_exhausted"`.

## Creative-Level Metrics

When the seller supports creative-level reporting (`supports_creative_breakdown` in reporting capabilities), each package includes a `by_creative` array with per-creative delivery metrics.

Each creative entry includes:

* **`creative_id`**: Creative identifier matching the creative assignment
* **`weight`**: Delivery weight for this creative during the reporting period (0-100)
* All standard delivery metrics (impressions, spend, clicks, ctr, etc.)

```json theme={null}
{
  "by_package": [
    {
      "package_id": "pkg_001",
      "spend": 5000,
      "impressions": 100000,
      "pricing_model": "cpm",
      "rate": 50,
      "currency": "USD",
      "delivery_status": "delivering",
      "by_creative": [
        {
          "creative_id": "hero_video_30s",
          "weight": 60,
          "impressions": 60000,
          "spend": 3000,
          "clicks": 3000,
          "ctr": 0.05,
          "completion_rate": 0.72
        },
        {
          "creative_id": "hero_video_15s",
          "weight": 40,
          "impressions": 40000,
          "spend": 2000,
          "clicks": 1200,
          "ctr": 0.03,
          "completion_rate": 0.85
        }
      ]
    }
  ]
}
```

For deeper creative analytics including variant-level delivery data (asset combination optimization, generative creative), use [`get_creative_delivery`](/dist/docs/3.2.0-beta.7/creative/task-reference/get_creative_delivery). This is a Creative Protocol task — call it on any agent that implements the Creative Protocol, which may be the same sales agent if it declares `"creative"` in `supported_protocols`. See [Creative capabilities on sales agents](/dist/docs/3.2.0-beta.7/creative/sales-agent-creative-capabilities).

By default `by_creative` is returned automatically at the seller's discretion, with no limit, sort, or truncation contract. To answer questions like "top creatives by completion rate," include `reporting_dimensions: { "creative": { "limit": 10, "sort_by": "quartile_100" } }` in the request — this negotiates the breakdown, making `by_creative_truncated`, `by_creative_sorted_by`, and `by_creative_sort_direction` binding. See [Dimension Breakdowns](#dimension-breakdowns).

## Catalog-item reporting

For catalog-driven packages (packages with a `catalog` field), the seller can return per-catalog-item delivery in the `by_catalog_item` array within each package.

Each entry identifies the catalog item and includes standard delivery metrics:

| Field             | Description                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------ |
| `content_id`      | The item identifier (SKU, GTIN, job ID, etc.)                                              |
| `content_id_type` | Identifier type (`sku`, `gtin`, `job_id`, etc.) matching the catalog's `content_id_type`   |
| Standard metrics  | `impressions`, `spend`, `clicks`, `ctr`, `conversions`, `roas`, and other delivery metrics |

This is optional. Sellers that support item-level reporting populate `by_catalog_item`; sellers that do not simply omit it.

By default `by_catalog_item` is returned automatically at the seller's discretion, with no limit, sort, or truncation contract. Including `reporting_dimensions.catalog_item` (e.g., `{ "limit": 25, "sort_by": "roas" }`) negotiates the breakdown, making `by_catalog_item_truncated`, `by_catalog_item_sorted_by`, and `by_catalog_item_sort_direction` binding. See [Dimension Breakdowns](#dimension-breakdowns).

```json theme={null}
{
  "by_package": [
    {
      "package_id": "pkg_001",
      "spend": 5000,
      "impressions": 100000,
      "pricing_model": "cpc",
      "rate": 1.20,
      "currency": "USD",
      "delivery_status": "delivering",
      "by_catalog_item": [
        {
          "content_id": "SKU-12345",
          "content_id_type": "sku",
          "impressions": 45000,
          "spend": 2250,
          "clicks": 1800,
          "ctr": 0.04,
          "conversions": 90,
          "roas": 4.2
        },
        {
          "content_id": "SKU-67890",
          "content_id_type": "sku",
          "impressions": 55000,
          "spend": 2750,
          "clicks": 2200,
          "ctr": 0.04,
          "conversions": 110,
          "roas": 3.8
        }
      ]
    }
  ]
}
```

## Windowed pull recovery

`reporting_webhook` fires at the buyer's chosen `reporting_frequency` (hourly through post\_campaign). When a receiver is offline long enough for transport retries to expire, the buyer loses per-window detail unless GET can reproduce the same slices. `time_granularity` + `include_window_breakdown` close that gap.

### Capability check

Sellers declare which granularities they honor for pull recovery via `reporting_capabilities.windowed_pull_granularities`. Buyers MUST check the capability before requesting `time_granularity`:

```json test=false theme={null}
{
  "reporting_capabilities": {
    "available_reporting_frequencies": ["hourly", "daily"],
    "windowed_pull_granularities": ["daily"]
  }
}
```

The seller in this example emits hourly webhooks but only honors daily pulls — common in stream-tap architectures where the webhook is a Kafka tap and historical pulls go through a warehouse. Two-paths-parity holds at the **declared** set; for hourly recovery, the webhook is primary. Sellers that want full parity declare every frequency they fire.

### Requesting windowed slices

```json test=false theme={null}
{
  "media_buy_ids": ["mb_12345"],
  "start_date": "2026-06-01",
  "end_date": "2026-06-02",
  "time_granularity": "hourly",
  "include_window_breakdown": true
}
```

### Response shape

Each media buy gains a `windows[]` array on the response:

```json test=false theme={null}
{
  "media_buy_deliveries": [
    {
      "media_buy_id": "mb_12345",
      "status": "active",
      "totals": { "impressions": 12345678, "spend": 5432.10 },
      "by_package": [ /* cumulative per-package — unchanged */ ],
      "windows": [
        {
          "window_start": "2026-06-01T00:00:00Z",
          "window_end": "2026-06-01T01:00:00Z",
          "totals": { "impressions": 510234, "spend": 226.05 },
          "by_package": [
            { "package_id": "pkg_001", "impressions": 510234, "spend": 226.05 }
          ],
          "is_final": true
        },
        {
          "window_start": "2026-06-01T01:00:00Z",
          "window_end": "2026-06-01T02:00:00Z",
          "totals": { "impressions": 488112, "spend": 215.83 },
          "is_final": true
        }
      ]
    }
  ]
}
```

Slices are ordered by `window_start` ascending; consecutive rows are contiguous (each row's `window_end` equals the next row's `window_start`). Each slice payload is shape-aligned with what `reporting_webhook` would have delivered for the same window — a buyer reconciling a missed webhook joins on `(media_buy_id, window_start)`.

### Spec contract

* **Capability-scoped MUST** — sellers MUST honor `time_granularity` requests for any value in `windowed_pull_granularities`. Pulls outside the declared set return [`UNSUPPORTED_GRANULARITY`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-unsupported-granularity).
* **Asymmetric is honest** — sellers MAY emit higher-frequency webhooks than they expose for pull. Declaring `available_reporting_frequencies: ["hourly", "daily"]` with `windowed_pull_granularities: ["daily"]` is valid; the buyer treats the hourly webhook as primary at that frequency.
* **Same-shape recovery** — slice payloads mirror webhook fire payloads at the same granularity so a buyer's reconciliation pipeline does not branch on transport path.

This surface anchors [snapshot-and-log](/dist/docs/3.2.0-beta.7/protocol/snapshot-and-log) Rule 4 (either path is complete) for data-bearing events. See that page for the broader contract.

## Dimension Breakdowns

When you include `reporting_dimensions` in the request, the response includes dimensional breakdown arrays within each `by_package` entry. Each breakdown entry inherits all fields from `delivery-metrics` plus dimension-specific identifiers.

### Requesting breakdowns

```json test=false theme={null}
{
  "media_buy_ids": ["mb_123"],
  "reporting_dimensions": {
    "geo": { "geo_level": "metro", "system": "nielsen_dma", "limit": 10 },
    "device_type": {},
    "format": { "sort_by": "spend" },
    "demographic": {
      "age_ranges": [{ "min": 26, "max": 30, "include_unknown": false }]
    },
    "placement": { "limit": 5, "sort_by": "roas" },
    "spot": {}
  }
}
```

Geo, device, format, audience, demographic, placement, creative, keyword, and catalog\_item dimensions accept optional `limit`, `sort_by` (any value from the `sort-metric` enum; defaults to `spend`), and `sort_direction` (`asc` or `desc`; defaults to `desc`, largest first). The sort vocabulary includes leaf metric identities that order rows by a value nested inside an object-shaped metric — `sort_by: "quartile_100"` orders by `quartile_data.q4_views`, `sort_by: "viewable_rate"` orders by `viewability.viewable_rate` — so queries like "top creatives by completion" need no flat duplicate fields. Survey/model-based lift scalars are excluded from sorting; see the `sort-metric` enum description. Setting `sort_direction: "asc"` enables bottom-N queries — for example, the 25 worst placements by `viewable_rate` — that cannot be recovered from a truncated descending pull. The seller echoes the sort it actually applied on each breakdown: `by_geo_sorted_by`/`by_geo_sort_direction`, `by_device_type_sorted_by`/`by_device_type_sort_direction`, and the equivalent pair for `device_platform`, `audience`, `demographic`, `placement`, `creative`, `keyword`, and `catalog_item`. For every dimension requested via `reporting_dimensions`, sellers MUST return both echo fields whenever the corresponding breakdown array is present, including when they fall back to sorting by `spend` because they don't report the requested metric — the echo makes that fallback visible instead of leaving the buyer to misread the rows as ordered by the requested metric. (When `creative`, `keyword`, or `catalog_item` rows are returned automatically without a request key, the echo and truncation fields are RECOMMENDED, not required — see the automatic-breakdown contract below.) Geo requires `geo_level` (`country`, `region`, `metro`, `postal_area`). Demographic accepts optional canonical `age_ranges`. Each requested range must be supported by the product's `supports_demographic_breakdown.age`: `exact_predicates` accepts ranges within its declared bounds, while `enumerated_intervals` accepts only exact declared intervals. Unsupported ranges return [`UNSUPPORTED_FEATURE`](/dist/docs/3.2.0-beta.7/building/verification/compliance-catalog#error-code-unsupported-feature); the seller never substitutes a wider or narrower bucket. Omitting `age_ranges` requests the product's native declared breakdown. The spot log accepts only `limit`; its canonical order is `aired_at` ascending. Breakdowns are per-dimension only — cross-dimensional intersections are not supported.

`creative`, `keyword`, and `catalog_item` are different from the other dimensions in one respect: sellers return them automatically whenever they support the underlying capability, whether or not the buyer's `reporting_dimensions` mentions them. Omitting the key preserves that automatic default — rows show up with no completeness contract, and `by_creative_truncated`/`by_keyword_truncated`/`by_catalog_item_truncated` and their sort echoes are only RECOMMENDED. Including the key (even as `{}`) negotiates the breakdown: `limit` and `sort_by`/`sort_direction` are honored, and the truncation flag and applied-sort echo become MUST-return fields, the same as any other requested dimension.

### Available dimensions

| Dimension       | Breakdown field      | Required fields                                             | Additional fields                                                      | Capability declaration                                                                                         |
| --------------- | -------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Geography       | `by_geo`             | `geo_level`, `geo_code`, `impressions`, `spend`             | `system`, `country`, `geo_name`                                        | `supports_geo_breakdown`                                                                                       |
| Device type     | `by_device_type`     | `device_type`, `impressions`, `spend`                       | —                                                                      | `supports_device_type_breakdown`                                                                               |
| Device platform | `by_device_platform` | `device_platform`, `impressions`, `spend`                   | —                                                                      | `supports_device_platform_breakdown`                                                                           |
| Creative format | `by_format`          | `format_kind`, `impressions`, `spend`                       | Other delivery metrics                                                 | `supports_format_breakdown`                                                                                    |
| Audience        | `by_audience`        | `audience_id`, `audience_source`, `impressions`, `spend`    | `audience_name`                                                        | `supports_audience_breakdown`                                                                                  |
| Demographic     | `by_demographic`     | `demographic`, `demographic_system`, `impressions`, `spend` | `age`, other delivery metrics                                          | `supports_demographic_breakdown`                                                                               |
| Placement       | `by_placement`       | `placement_id`, `impressions`, `spend`                      | `publisher_domain`, `placement_name`                                   | `supports_placement_breakdown`                                                                                 |
| Spot/as-run     | `by_spot`            | `spot_id`, `aired_at`                                       | `impressions`, `network`, `station`, `daypart`, other delivery metrics | `supports_spot_breakdown`                                                                                      |
| Creative        | `by_creative`        | `creative_id`, `impressions`, `spend`                       | `weight`, other delivery metrics                                       | `supports_creative_breakdown` (automatic; `reporting_dimensions.creative` optionally negotiates limit/sort)    |
| Keyword         | `by_keyword`         | `keyword`, `match_type`, `impressions`, `spend`             | other delivery metrics                                                 | Search/retail media capability (automatic; `reporting_dimensions.keyword` optionally negotiates limit/sort)    |
| Catalog item    | `by_catalog_item`    | `content_id`, `content_id_type`, `impressions`, `spend`     | other delivery metrics                                                 | Catalog-driven packages only (automatic; `reporting_dimensions.catalog_item` optionally negotiates limit/sort) |

Check `reporting_capabilities` on the product to discover which dimensions are available. Product-level capabilities are authoritative since different products from the same seller may support different breakdowns.

`by_format` uses the AdCP 3.1 canonical `format_kind` vocabulary. It answers cross-kind questions such as how `video_hosted`, `native_in_feed`, and `image` delivery compare; it does not distinguish two creatives that share a format kind but differ by duration, dimensions, or another format-option parameter. Sellers MUST combine adopter-defined shapes into one `custom` row. When `by_format_truncated` is false, additive metrics such as impressions and spend SHOULD reconcile to package totals, subject to each metric's measurement and attribution semantics. `by_format_sorted_by` and `by_format_sort_direction` echo the sort actually applied, so a fallback to `spend` is visible rather than silent. Buyers MUST NOT expect row-level correspondence between `by_format` and `by_creative` because they are independently produced at different grains. Requested dimensional breakdowns, including `by_format`, are negotiated on the GET path. Reporting webhook configuration does not negotiate or guarantee them, although webhook payloads MAY carry the same fields as extensions. Requested dimensions do not apply to `windows[].by_package`; those rows remain shape-aligned with webhook fires for pull recovery.

`demographic_targeting` declares what the product can execute; `supports_demographic_breakdown` separately declares what it can measure and report. A product can therefore target 26–30 but disclose fixed 25–34 reporting. Buyers MUST NOT infer exact reporting from targeting capability or parse equivalence from a native label. `demographic` uses the notation of its accompanying `demographic_system`; canonical `age`, when present, is the authoritative machine-comparable interval.

`supports_geo_breakdown`, `supports_demographic_breakdown`, and `supports_spot_breakdown` are structured capability objects; the other declarations in this table are boolean flags. Demographic capability declares exact predicates versus fixed intervals, available measurement systems, and whether small cells may be suppressed. `supports_spot_breakdown.available_metrics` lists metrics available at spot grain; an empty array means the product supplies as-run airing evidence but no per-spot delivery metrics.

Placement identity is publisher-scoped. Placement rows MAY carry `publisher_domain` — the publisher namespace from the product's `placements[]` entry — so buyers can treat `{publisher_domain, placement_id}` as the stable placement identity for multi-publisher products when it is present. Sellers SHOULD emit `publisher_domain` whenever the product placement carries it (always true for `kind: "publisher_ref"`); sellers MAY omit it only for `kind: "seller_inline"` placements in a legacy single-publisher context where the seller agent's own domain is the namespace. When `publisher_domain` is omitted, buyers MAY interpret `placement_id` relative to the seller agent's own publisher domain only in that legacy single-publisher context; otherwise buyers should not infer a cross-publisher placement key. `publisher_domain` is single-valued because each placement belongs to exactly one publisher namespace.

Spot rows are channel-neutral as-run records. Linear TV commonly supplies `network`, radio commonly supplies `station`, and sellers MAY populate both; neither is required. `spot_id` identifies the scheduled occurrence and remains stable when the same airing is re-reported across Live, C3, C7, or another measurement-window progression. `aired_at` proves that it aired. Sellers MUST order rows by `aired_at` ascending. When `reporting_dimensions.spot.limit` is omitted, sellers SHOULD return the complete log for the requested reporting period. `by_spot_truncated` tells the buyer whether the returned log is complete.

Delivery metrics on a spot row are optional. Omission means the metric is not yet available in the package's current `measurement_window`, or the product did not declare it in `supports_spot_breakdown.available_metrics`. A later window may return the same `spot_id` with impressions populated. An explicit `impressions: 0` is a measured zero; it does not mean pending or unavailable, and `null` is invalid.

### Truncation

Each breakdown array has a sibling boolean flag (e.g., `by_geo_truncated`). When `true`, additional rows exist beyond the returned set. When `false`, the list is complete. For every dimension requested via `reporting_dimensions`, sellers MUST return the truncated flag whenever the corresponding breakdown array is present. Metric dimensions are sorted by the requested `sort_by` value in the requested `sort_direction` (descending by default); the seller echoes the metric and direction it actually applied in `by_*_sorted_by`/`by_*_sort_direction`, which sellers MUST return whenever a requested dimension's breakdown array is present. For `by_creative`, `by_keyword`, and `by_catalog_item` rows returned automatically without a request key, the truncated and echo fields are RECOMMENDED rather than required — automatic rows carry no completeness contract. Spot rows always use `aired_at` ascending and carry neither field.

Demographic reporting also returns `by_demographic_suppressed`. `true` means privacy, policy, or measurement thresholds omitted otherwise reportable rows, so the visible rows may not reconcile to package totals. This is distinct from truncation caused by a row limit.

### Audience sources

The `audience_source` field indicates where the audience segment originated:

| Source        | Description                                                                                                      | Targetable?                                     |
| ------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `synced`      | Buyer's first-party data via [`sync_audiences`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/sync_audiences) | Yes — use `audience_include`/`audience_exclude` |
| `platform`    | Seller's native segments (interest, behavioral)                                                                  | No — informational                              |
| `third_party` | External data provider segments                                                                                  | No — informational                              |
| `lookalike`   | Platform-generated expansion from a seed                                                                         | No — informational                              |
| `retargeting` | Prior engagement via seller's pixel/tag                                                                          | No — informational                              |
| `unknown`     | Unclassified or unrecognized audience source                                                                     | No — informational                              |

## Best Practices

**1. Check Date Range Support**
Before requesting date-filtered delivery, check `reporting_capabilities.date_range_support` on the product. Products with `lifetime_only` support reject date range requests — omit `start_date` and `end_date` to get campaign lifetime data instead.

**2. Use Date Ranges for Analysis**
For products that support date ranges, specify dates for period-over-period comparisons and trend analysis.

**3. Monitor Pacing Index**
Aim for 0.95-1.05 pacing index. Values outside this range indicate delivery issues.

**4. Check Daily Breakdown**
Identify delivery patterns and weekend/weekday performance differences.

**5. Compare Package Performance**
Use `by_package` breakdowns to identify best-performing inventory. Check both `paused` state and `delivery_status` to understand why packages aren't delivering.

**6. Track Status Changes**
Use multi-status queries to understand why campaigns were paused or completed.

## Post-Delivery Governance Validation

Delivery reporting is not the final step. When campaign governance is active, delivery data feeds into governance validation to detect unauthorized supply paths, geo drift, and pacing violations.

The governance feedback loop:

1. Pull delivery data via `get_media_buy_delivery`
2. Report outcomes to the governance agent via [`report_plan_outcome`](/dist/docs/3.2.0-beta.7/governance/campaign/tasks/report_plan_outcome)
3. The governance agent compares actual delivery against planned parameters (drift detection)
4. Validate property delivery via [`validate_property_delivery`](/dist/docs/3.2.0-beta.7/governance/property/tasks/validate_property_delivery) to catch unauthorized supply paths

| Governance task                                                                                                     | Purpose                                                                                           |
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [`report_plan_outcome`](/dist/docs/3.2.0-beta.7/governance/campaign/tasks/report_plan_outcome)                      | Feed delivery data to the governance agent for budget tracking and drift detection                |
| [`validate_property_delivery`](/dist/docs/3.2.0-beta.7/governance/property/tasks/validate_property_delivery)        | Validate delivery records against property lists — catches ads running on unauthorized properties |
| [`validate_content_delivery`](/dist/docs/3.2.0-beta.7/governance/content-standards/tasks/validate_content_delivery) | Validate content artifacts against brand suitability standards                                    |
| [`get_plan_audit_logs`](/dist/docs/3.2.0-beta.7/governance/campaign/tasks/get_plan_audit_logs)                      | View the full plan state and audit trail                                                          |

Without this feedback loop, delivery data is reported but never validated. Budget overruns, pacing divergence, geo drift, and unauthorized supply paths go undetected.

## Next Steps

After retrieving delivery data:

1. **Optimize Campaigns**: Use [`update_media_buy`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/update_media_buy) to adjust budgets, pacing, or targeting
2. **Provide Feedback**: Use [`provide_performance_feedback`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/provide_performance_feedback) to share results with seller
3. **Update Creatives**: Use [`sync_creatives`](/dist/docs/3.2.0-beta.7/creative/task-reference/sync_creatives) for library-backed sellers, or inline `packages[].creatives` on [`update_media_buy`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/update_media_buy) for inline-only sellers
4. **Create Follow-Up Campaigns**: Use [`create_media_buy`](/dist/docs/3.2.0-beta.7/media-buy/task-reference/create_media_buy) based on insights

## Learn More

* [Media Buy Lifecycle](/dist/docs/3.2.0-beta.7/media-buy/media-buys/) - Complete campaign workflow
* [Async Operations](/dist/docs/3.2.0-beta.7/building/by-layer/L3/async-operations) - Async patterns and status handling
* [Performance Optimization](/dist/docs/3.2.0-beta.7/media-buy/media-buys/optimization-reporting) - Using delivery data for optimization
