Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-format-delivery-breakdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"adcontextprotocol": minor
---

Add capability-gated delivery breakdowns by canonical creative `format_kind` to `get_media_buy_delivery`, including explicit GET-only scope, `custom` aggregation, truncation disclosure, and independent reconciliation from creative-level rows.
3 changes: 3 additions & 0 deletions docs/media-buy/media-buys/optimization-reporting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1275,13 +1275,16 @@ Delivery data can be broken down across multiple dimensions within each package.
| 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` |

Each breakdown entry inherits all fields from `delivery-metrics` (clicks, conversions, and other optional metrics) plus its dimension-specific fields. Every entry requires the fields shown in the required fields column. Check `reporting_capabilities` on the product to discover which dimensions are available; product-level capabilities are authoritative because different products from the same seller may support different breakdowns. `supports_geo_breakdown`, `supports_demographic_breakdown`, and `supports_spot_breakdown` are structured declarations; 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.

Format breakdowns use the AdCP 3.1 canonical `format_kind` vocabulary and are negotiated on the GET path. Reporting webhook configuration does not negotiate or guarantee dimensional breakdowns, although webhook payloads may carry the same fields as extensions. They compare creative shapes such as `video_hosted`, `native_in_feed`, and `image`; duration, dimensions, and other format-option parameters are outside this grain. Sellers 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. Buyers must not expect row-level correspondence between `by_format` and `by_creative` because they are independently produced at different grains.

Demographic targeting and demographic reporting are separate product capabilities. A product may execute an exact buyer predicate while its measurement source reports only fixed buckets. For example, a social product might declare continuous targeting for ages 18–65 but the following reporting capability:

```json test=false
Expand Down
8 changes: 6 additions & 2 deletions docs/media-buy/task-reference/get_media_buy_delivery.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Retrieve comprehensive delivery metrics and performance data for media buy repor
| `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`, `demographic`, `placement`, `spot`. Most accept optional `limit` and `sort_by`. 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. Unsupported dimensions are silently omitted; unsupported requested demographic ranges return [`UNSUPPORTED_FEATURE`](/docs/building/verification/compliance-catalog#error-code-unsupported-feature); malformed requests return a validation error. |
| `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`. Most accept optional `limit` and `sort_by`. `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. Unsupported dimensions are silently omitted; unsupported requested demographic ranges return [`UNSUPPORTED_FEATURE`](/docs/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. |

Expand Down Expand Up @@ -886,6 +886,7 @@ When you include `reporting_dimensions` in the request, the response includes di
"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 }]
},
Expand All @@ -895,7 +896,7 @@ When you include `reporting_dimensions` in the request, the response includes di
}
```

Geo, device, audience, demographic, and placement dimensions accept optional `limit` and `sort_by` (any value from the `sort-metric` enum; defaults to `spend`). 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`](/docs/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.
Geo, device, format, audience, demographic, and placement dimensions accept optional `limit` and `sort_by` (any value from the `sort-metric` enum; defaults to `spend`). 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`](/docs/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.

### Available dimensions

Expand All @@ -904,13 +905,16 @@ Geo, device, audience, demographic, and placement dimensions accept optional `li
| 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` |

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. 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.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"deploy:cdn-artifacts-cutover:dry-run": "wrangler deploy --config workers/artifact-cdn/wrangler.cutover.toml --dry-run",
"verify:cdn-artifacts-cutover": "node scripts/verify-cdn-artifacts-cutover.mjs",
"typecheck": "tsc --project server/tsconfig.json --noEmit",
"test:schemas": "node tests/schema-validation.test.cjs && node --test tests/trusted-match-offer-creative-data.test.cjs tests/accessibility-violation-details.test.cjs tests/portfolio-routing-scope.test.cjs tests/catalog-item-availability-updates.test.cjs tests/schema-deprecation-metadata.test.cjs tests/creative-rotation.test.cjs tests/lint-schema-enum-drift.test.cjs tests/synthetic-depiction.test.cjs && npm run test:geo-region-targeting",
"test:schemas": "node tests/schema-validation.test.cjs && node --test tests/trusted-match-offer-creative-data.test.cjs tests/accessibility-violation-details.test.cjs tests/portfolio-routing-scope.test.cjs tests/catalog-item-availability-updates.test.cjs tests/schema-deprecation-metadata.test.cjs tests/creative-rotation.test.cjs tests/format-delivery-reporting-contract.test.cjs tests/lint-schema-enum-drift.test.cjs tests/synthetic-depiction.test.cjs && npm run test:geo-region-targeting",
"test:performance-feedback": "node --test --test-force-exit --test-timeout=30000 tests/performance-feedback-contract.test.cjs",
"test:dist-schema-version-ids": "node --test --test-force-exit --test-timeout=30000 tests/dist-schema-version-ids.test.cjs",
"test:examples": "node tests/example-validation-simple.test.cjs && npm run test:tmp-context-merge",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
}
},
"supports_creative_breakdown": { "type": "boolean" },
"supports_format_breakdown": { "type": "boolean" },
"supports_keyword_breakdown": { "type": "boolean" },
"supports_geo_breakdown": { "$ref": "/schemas/core/geo-breakdown-support.json" },
"supports_device_type_breakdown": { "type": "boolean" },
Expand Down
4 changes: 4 additions & 0 deletions static/schemas/source/core/reporting-capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
"type": "boolean",
"description": "Whether this product supports creative-level metric breakdowns in delivery reporting (by_creative within by_package)"
},
"supports_format_breakdown": {
"type": "boolean",
"description": "Whether this product supports canonical creative-format breakdowns in GET delivery reporting (by_format within by_package, keyed by format_kind). This is independent from supports_creative_breakdown because a seller may expose aggregate format-grain reporting without exposing individual creative performance."
},
"supports_keyword_breakdown": {
"type": "boolean",
"description": "Whether this product supports keyword-level metric breakdowns in delivery reporting (by_keyword within by_package)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,23 @@
},
"additionalProperties": true
},
"format": {
"type": "object",
"description": "Request delivery broken down by canonical creative format kind. This dimension is negotiated on the GET path. Reporting webhook configuration does not negotiate or guarantee dimensional breakdowns, although a webhook payload may carry the same fields as an extension.",
"properties": {
"limit": {
"type": "integer",
"minimum": 1,
"description": "Maximum number of format rows to return. When omitted, all rows are returned because the canonical format-kind vocabulary is small and bounded."
},
"sort_by": {
"$ref": "/schemas/enums/sort-metric.json",
"description": "Metric to sort breakdown rows by (descending). Falls back to 'spend' if the seller does not report the requested metric.",
"default": "spend"
}
},
"additionalProperties": true
},
"audience": {
"type": "object",
"description": "Request audience segment breakdown.",
Expand Down
Loading
Loading