> ## 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`](/docs/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` for an account-owned buy.

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

## Request Parameters

| Parameter                  | Type                                                                             | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| -------------------------- | -------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `account`                  | [account-ref](/docs/building/integration/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`, `audience`, `placement`. Each accepts optional `limit` (defaults to 25 for geo, audience, placement) and `sort_by` (sort-metric enum, default: `spend`). Geo requires `geo_level` (one per request); include `system` for metro/postal levels when requesting a specific system. Unsupported dimensions are silently omitted; 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`, `monthly`). 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.                                                                                                                                                                                                                                                                                                                                                                                                         |

> **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"`.

**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` and `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`](/docs/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/v3/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`](/docs/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`](/docs/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`](/docs/media-buy/advanced-topics/accountability) and out-of-band negotiation.

See [Billing authority](/docs/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`](/docs/media-buy/task-reference/create_media_buy) and [`get_media_buys`](/docs/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`](/docs/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`)

### 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 a `DATE_RANGE_NOT_SUPPORTED` 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

## 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](/docs/media-buy/advanced-topics/accountability) for measurement terms and [Optimization and reporting](/docs/media-buy/media-buys/optimization-reporting) for the full lifecycle.

## Error Handling

| Error Code                 | Description                                                                        | Resolution                                                                                                                                                                                               |
| -------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AUTH_MISSING`             | No credentials presented                                                           | Provide credentials via auth header                                                                                                                                                                      |
| `AUTH_INVALID`             | Credentials rejected (expired / revoked)                                           | Human credential rotation required; do not auto-retry                                                                                                                                                    |
| `MEDIA_BUY_NOT_FOUND`      | Media buy doesn't exist                                                            | Verify `media_buy_id`; for legacy correlation use `get_media_buys` + `context.internal_campaign_id`                                                                                                      |
| `INVALID_DATE_RANGE`       | Invalid start/end dates                                                            | Use YYYY-MM-DD format, ensure start \< end                                                                                                                                                               |
| `DATE_RANGE_NOT_SUPPORTED` | Product only supports lifetime reporting                                           | Omit `start_date` and `end_date`. Check `reporting_capabilities.date_range_support` on the product.                                                                                                      |
| `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). |
| `CONTEXT_REQUIRED`         | No media buys in context                                                           | Provide media\_buy\_ids explicitly                                                                                                                                                                       |
| `INVALID_STATUS_FILTER`    | Invalid status value                                                               | Use valid status: pending\_creatives, pending\_start, active, paused, completed                                                                                                                          |

## 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
  * `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, etc.)

**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`](/docs/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](/docs/creative/sales-agent-creative-capabilities).

## 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.

```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, daily, monthly). 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`.
* **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](/docs/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": {},
    "placement": { "limit": 5, "sort_by": "roas" }
  }
}
```

Each dimension accepts optional `limit` (max rows; defaults to 25 for geo, audience, and placement) and `sort_by` (any value from the `sort-metric` enum, e.g., `spend`, `impressions`, `clicks`, `roas` — defaults to `spend` descending; falls back to `spend` if the seller does not report the requested metric). Geo requires `geo_level` (`country`, `region`, `metro`, `postal_area`). Include `system` for metro/postal levels when requesting a specific system; native postal requests also include `country` (for example, `{ "geo_level": "postal_area", "country": "US", "system": "zip" }`). Each request uses a single geo\_level — for multiple granularities (e.g., country and region), make separate requests. Unsupported dimensions are silently omitted from the response, but malformed requests (e.g., geo without `geo_level`) return a validation error. Breakdowns are per-dimension only — cross-dimensional intersections (e.g., device\_type × geo) are not supported.

### 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` |
| Audience        | `by_audience`        | `audience_id`, `audience_source`, `impressions`, `spend` | `audience_name`                      | `supports_audience_breakdown`        |
| Placement       | `by_placement`       | `placement_id`, `impressions`, `spend`                   | `publisher_domain`, `placement_name` | `supports_placement_breakdown`       |

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.

`supports_geo_breakdown` is an object that declares available levels and systems; the other capability declarations in this table are boolean flags. Within `supports_geo_breakdown`, `country` and `region` are booleans, while `metro` is keyed by `metro-system` values and native `postal_area` is keyed by ISO 3166-1 alpha-2 country with arrays of country-local `postal-system` values. Geo rows use `system` for `geo_level: "metro"` and `"postal_area"`; native postal rows also include `country`. Deprecated country-fused postal systems remain accepted for compatibility.

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.

### 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. Sellers MUST return the truncated flag whenever the corresponding breakdown array is present. Rows are sorted by the requested `sort_by` metric descending.

### Audience sources

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

| Source        | Description                                     | Targetable?                                     |
| ------------- | ----------------------------------------------- | ----------------------------------------------- |
| `synced`      | Buyer's first-party data via `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`](/docs/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`](/docs/governance/property/tasks/validate_property_delivery) to catch unauthorized supply paths

| Governance task                                                                                   | Purpose                                                                                           |
| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| [`report_plan_outcome`](/docs/governance/campaign/tasks/report_plan_outcome)                      | Feed delivery data to the governance agent for budget tracking and drift detection                |
| [`validate_property_delivery`](/docs/governance/property/tasks/validate_property_delivery)        | Validate delivery records against property lists — catches ads running on unauthorized properties |
| [`validate_content_delivery`](/docs/governance/content-standards/tasks/validate_content_delivery) | Validate content artifacts against brand suitability standards                                    |
| [`get_plan_audit_logs`](/docs/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`](/docs/media-buy/task-reference/update_media_buy) to adjust budgets, pacing, or targeting
2. **Provide Feedback**: Use [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) to share results with seller
3. **Update Creatives**: Use [`sync_creatives`](/docs/creative/task-reference/sync_creatives) for library-backed sellers, or inline `packages[].creatives` on [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) for inline-only sellers
4. **Create Follow-Up Campaigns**: Use [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) based on insights

## Learn More

* [Media Buy Lifecycle](/docs/media-buy/media-buys/) - Complete campaign workflow
* [Async Operations](/docs/building/implementation/async-operations) - Async patterns and status handling
* [Performance Optimization](/docs/media-buy/media-buys/optimization-reporting) - Using delivery data for optimization
