Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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-spot-as-run-reporting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"adcontextprotocol": minor
---

Add opt-in spot-level as-run delivery reporting for scheduled inventory, with stable spot identity, actual airing time, and explicit product capability for metrics available at spot grain.
2 changes: 1 addition & 1 deletion docs/contributing/x-entity-annotation.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ High-level groupings (see the registry for full descriptions). *Categories below
|---|---|
| Brand & rights | `advertiser_brand`, `rights_holder_brand`, `rights_grant` |
| Account & party | `account`, `operator` |
| Media buy | `media_buy`, `package`, `product`, `proposal`, `product_pricing_option` |
| Media buy | `media_buy`, `package`, `product`, `proposal`, `product_pricing_option`, `spot_airing` |
| Creative | `creative`, `creative_format` |
| Data & targeting | `audience`, `audience_evidence`, `audience_evidence_snapshot`, `signal`, `signal_activation_id`, `demographic_interval_id`, `event_source` |
| Lists & catalogs | `collection_list`, `property_list`, `catalog`, `property` |
Expand Down
7 changes: 6 additions & 1 deletion docs/media-buy/media-buys/optimization-reporting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1278,8 +1278,9 @@ Delivery data can be broken down across multiple dimensions within each package.
| 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` and `supports_demographic_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.
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.

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:

Expand Down Expand Up @@ -1308,6 +1309,10 @@ Demographic rows pair a free-form `demographic` code with its `demographic_syste

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: TV sellers commonly populate `network`, radio sellers commonly populate `station`, and either may populate both. `spot_id` is stable across measurement-window updates; `aired_at` proves the airing. Sellers MUST order rows by `aired_at` ascending. An empty `reporting_dimensions.spot` request asks for the complete log for the reporting period; an optional `limit` may bound the response. `by_spot_truncated` indicates whether the log is complete.

Spot metrics are optional because airing evidence and audience measurement mature independently. `supports_spot_breakdown.available_metrics` tells the buyer which metrics the product can eventually produce at spot grain. A Live row may contain only `{ spot_id, aired_at }`; the same `spot_id` can reappear under C3 or C7 with `impressions`. An absent `impressions` field means unavailable at that measurement window or not declared at spot grain. `impressions: 0` means a measured zero and is never interchangeable with omission; sellers do not emit `null`.

Breakdowns are opt-in — no dimension data is returned unless explicitly requested. Sellers that don't support a requested dimension silently omit it. An unsupported `demographic.age_ranges` value on a product that otherwise supports the dimension returns `UNSUPPORTED_FEATURE`; sellers never widen or narrow it silently. Each breakdown array has a sibling `by_*_truncated` boolean indicating whether additional non-suppressed rows exist beyond the requested `limit`.

## Targeting Consistency
Expand Down
16 changes: 11 additions & 5 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`. 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. 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`, `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. |
| `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 @@ -889,12 +889,13 @@ When you include `reporting_dimensions` in the request, the response includes di
"demographic": {
"age_ranges": [{ "min": 26, "max": 30, "include_unknown": false }]
},
"placement": { "limit": 5, "sort_by": "roas" }
"placement": { "limit": 5, "sort_by": "roas" },
"spot": {}
}
}
```

Most 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. Breakdowns are per-dimension only — cross-dimensional intersections are not supported.
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.

### Available dimensions

Expand All @@ -906,18 +907,23 @@ Most dimensions accept optional `limit` and `sort_by` (any value from the `sort-
| 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.

`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` and `supports_demographic_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_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. Sellers MUST return the truncated flag whenever the corresponding breakdown array is present. Rows are sorted by the requested `sort_by` metric descending.
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. Metric dimensions are sorted by the requested `sort_by` value descending; spot rows always use `aired_at` ascending.

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.

Expand Down
1 change: 1 addition & 0 deletions scripts/x-entity-field-map.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"product_id": "product",
"proposal_id": "proposal",
"opportunity_id": "opportunity",
"spot_id": "spot_airing",
"creative_id": "creative",
"audience_id": "audience",
"evidence_id": "audience_evidence",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"supports_audience_breakdown": { "type": "boolean" },
"supports_demographic_breakdown": { "$ref": "/schemas/core/demographic-reporting-capability.json" },
"supports_placement_breakdown": { "type": "boolean" },
"supports_spot_breakdown": { "$ref": "/schemas/core/spot-reporting-capability.json" },
"date_range_support": { "type": "string", "enum": ["date_range", "lifetime_only"] },
"windowed_pull_granularities": { "type": "array", "items": { "$ref": "/schemas/enums/reporting-frequency.json" }, "uniqueItems": true },
"measurement_windows": { "type": "array", "items": { "$ref": "/schemas/core/measurement-window.json" }, "minItems": 1, "uniqueItems": true }
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 @@ -110,6 +110,10 @@
"type": "boolean",
"description": "Whether this product supports placement breakdowns in delivery reporting (by_placement within by_package)"
},
"supports_spot_breakdown": {
"$ref": "/schemas/core/spot-reporting-capability.json",
"description": "Spot-level as-run airing-log support and metrics available at spot grain for broadcast TV, radio, and other scheduled inventory."
},
"date_range_support": {
"type": "string",
"enum": ["date_range", "lifetime_only"],
Expand Down
19 changes: 19 additions & 0 deletions static/schemas/source/core/spot-reporting-capability.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/core/spot-reporting-capability.json",
"title": "Spot Reporting Capability",
"description": "Product-scoped spot-level as-run reporting capability. Presence means the seller can report stable spot identities and actual airing timestamps. available_metrics declares which delivery metrics may eventually be populated at spot grain; an empty array means the product provides airing evidence only.",
"type": "object",
"properties": {
"available_metrics": {
"type": "array",
"description": "Metrics available at spot grain. A declared metric may be absent from a provisional measurement window and appear when a later window supersedes it. A metric omitted from this declaration is not promised at spot grain even when it is available at package grain.",
"items": {
"$ref": "/schemas/enums/available-metric.json"
},
"uniqueItems": true
}
},
"required": ["available_metrics"],
"additionalProperties": false
}
Loading
Loading