Skip to content
Open
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
16 changes: 16 additions & 0 deletions .changeset/geo-place-targeting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"adcontextprotocol": minor
---

Add identifier-based named-place geographic targeting:

- `targeting_overlay.geo_places` and `geo_places_exclude` carry stable identifiers with country, system, place type, optional catalog version, and diagnostic labels.
- `get_adcp_capabilities` declares exact country/type pairs, accepted catalog versions, and a standard resolver for every collision-safe identifier system.
- `get_products.targeting_overlay` carries known place IDs so configured products, pricing, and forecasts reflect them; `required_overlay_support` and Product `overlay_support` declare collision-safe permission for place values selected later.
- Package status MUST echo persisted place overlays with the applied catalog version through the existing `targeting_overlay` contract.
- Resolver responses echo their normalized query, carry machine-verifiable disambiguation and lifecycle metadata, and support existing-ID refresh after catalog rollover.
- `PLACE_TARGET_UNAVAILABLE` provides a nonfatal, correctable read-path signal when a pinned target can no longer execute without silently changing geography.
- Create-time place overlays use deterministic `UNSUPPORTED_FEATURE`, `INVALID_REQUEST`, `TARGETING_TOO_NARROW`, and `REQUOTE_REQUIRED` dispositions; the latter two now explicitly cover zero-inventory and out-of-envelope create-time targeting.
- Place forecast and delivery breakdowns remain deferred; package echo is the interim configuration-audit path.

Refs #5588.
58 changes: 56 additions & 2 deletions docs/media-buy/advanced-topics/targeting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,11 @@ from prose should be confirmed separately in
`required_overlay_support` asks whether a product lets the buyer choose a
dimension later. For example, requesting `geo_metros` support asks for products
that can be narrowed by metro on packages; it does not ask the seller to return
one product per metro. The product answers with `overlay_support`.
one product per metro. Named places follow the same rule: known IDs belong in
`targeting_overlay.geo_places`, while a buyer that will choose them later asks
for `required_overlay_support.geo_places`. The product answers with binding
selectable permission in `overlay_support`; that permission is not a
value-specific availability guarantee.

Coverage is an implementation detail of the same targeting contract. A
constraint may be satisfied by the product's inherent inventory scope or by a
Expand Down Expand Up @@ -456,12 +460,14 @@ Use geo fields **only** for:
- `geo_regions`: ISO 3166-2 subdivision codes (e.g., `["US-CA", "GB-SCT"]`)
- `geo_metros`: Structured metro areas with explicit system (e.g., `nielsen_dma`, `uk_itl2`) — not all publishers support metro-level targeting
- `geo_postal_areas`: Structured postal areas with explicit country and system (e.g., `US` / `zip`, `GB` / `outward`, `ZA` / `postal_code`) — not all publishers support postal-level targeting
- `geo_places`: Catalog-backed named places with explicit country, identifier system, place type, and stable IDs — use this for platform place entities such as cities or municipalities, not raw place names

**Exclusion fields** (exclude these locations from delivery):
- `geo_countries_exclude`: Same format as `geo_countries`
- `geo_regions_exclude`: Same format as `geo_regions`
- `geo_metros_exclude`: Same format as `geo_metros`
- `geo_postal_areas_exclude`: Same format as `geo_postal_areas`
- `geo_places_exclude`: Same format as `geo_places`

**Note**: Inclusion and exclusion can be combined. Metro and postal targeting require specifying the classification system, enabling international support. Not all geographic granularities are supported by all publishers. Country and region are most widely supported.

Expand Down Expand Up @@ -742,6 +748,54 @@ Geographic targeting supports both inclusion (restrict to) and exclusion (exclud
- **Use cases**: RCT holdout zip codes, restricted delivery areas
- **Note**: Seller must declare supported systems in `get_adcp_capabilities`; the deprecated legacy form remains accepted during the 3.x migration.

### geo_places

- **Description**: Restrict delivery to named administrative or local places represented by stable catalog identifiers
- **Format**: Array of objects with required `country`, `system`, `place_type`, and `values`; optional `system_version`, `value_labels`, and `ext`
- **Systems**: Registered namespaces are `geonames`, `google_ads`, and `microsoft_ads`. Other catalogs use an owner-controlled absolute HTTPS URI, such as `https://seller.example/geo/catalogs/places`. MaxMind `geoname_id` values use the `geonames` namespace; MaxMind is a catalog source/version, not a separate identifier namespace.
- **Example**: `[{ "country": "NL", "system": "geonames", "system_version": "2026-05", "place_type": "city", "values": ["2759794"], "value_labels": { "2759794": "Amsterdam, North Holland, Netherlands" } }]`
- **Use cases**: Target a platform's named city, municipality, borough, neighborhood, post town, city region, or county entity without relying on ambiguous names
- **Note**: `values` are the authoritative targeting keys. Every `value_labels` key MUST appear in `values`; labels exist only for diagnostics and audit readability, and sellers MUST NOT resolve or apply targeting from them. Raw names such as `Amsterdam` are unresolved intent, not valid values.

Within `geo_places`, values and entries have union semantics: delivery may occur in any included place. `geo_places_exclude` subtracts matching places from the current candidate geography, including when no `geo_places` inclusion is present. Inclusion across different geographic dimensions is intersected. Sellers MUST reject the same `(country, system, place_type, value)` in both include and exclude lists, even when the include and exclude entries specify different catalog versions: version is not part of stable place identity. Sellers MAY reject cross-level combinations they cannot resolve safely rather than silently approximating them.

Before sending a place target, buyers inspect `get_adcp_capabilities.media_buy.execution.targeting.geo_places`. Support is declared as exact country/type pairs, not independent lists. Each system also declares `catalog.current_version`, exact `supported_versions`, and an `adcp_geo_place_resolver_v1` endpoint. Buyers resolve raw names—or refresh an existing ID—using an HTTPS GET with exactly one of `q` or `value` from `get-geo-place-resolution-request.json`. The response echoes the normalized request and follows `get-geo-place-resolution-response.json`, carrying machine-readable country/subdivision/type context plus active, removal-planned, or deprecated identifiers and replacements. Ambiguous results require user or agent disambiguation before trafficking.

If `system_version` is omitted from a new target, the seller applies its declared `current_version`. Sellers MUST reject unsupported systems, country/type pairs, versions, deprecated identifiers, and unknown identifiers rather than silently dropping or reinterpreting them. If an identifier is stale, the seller returns a validation error and may surface resolver-provided replacements; it MUST NOT silently substitute a replacement. Sellers MUST echo persisted `geo_places` and `geo_places_exclude` in package `targeting_overlay` state with the exact applied `system_version` and values.

Accepted place targeting is pinned to the echoed `system_version` for the life of the package. Removing that version from `supported_versions` stops new targeting and target-changing updates from using it, but MUST NOT silently mutate, drop, or invalidate an existing package. An unrelated package update preserves the pinned place overlay. If a seller can no longer execute a pinned target, `get_media_buys` MUST still echo it and return a nonfatal `errors[]` entry with `code: "PLACE_TARGET_UNAVAILABLE"`, `recovery: "correctable"`, `field` pointing to the exact `media_buys[N].packages[M].targeting_overlay.geo_places[_exclude][A].values[V]` response path, and `details` containing `media_buy_id`, `package_id`, `system`, `system_version`, `country`, `place_type`, and `value`. The buyer can use resolver `value` lookup against the current catalog to find lifecycle status and proposed replacements, then submit an intentional target update.

Place forecast and delivery breakdown rows are intentionally not part of this release: `geo_level: "place"` remains invalid on reporting surfaces. Package-state echo provides configuration auditability, but not delivery-by-place verification. Place-level forecast, delivery, pacing, and reconciliation require a follow-up reporting RFC.

Known place IDs belong in `get_products.targeting_overlay.geo_places`, so every
returned product, price, and aggregate forecast reflects that effective
targeting even though a place-level breakdown is unavailable. If IDs will be
chosen on packages later, the buyer requests `required_overlay_support.geo_places`
(and independently `geo_places_exclude`) with the required system,
country/type pairs, and optional catalog versions. Returned Product
`overlay_support` is binding selectable permission, not a value-specific
inventory, price, or forecast guarantee. At create, the seller applies this
deterministic disposition matrix to the actual place overlay:

| Condition | Result |
|---|---|
| The dimension, identifier system, country, place type, or combination is outside the selected Product `overlay_support` | `UNSUPPORTED_FEATURE` |
| The tuple is supported, but an ID is invalid, unknown, or deprecated, or an explicit `system_version` is not supported | `INVALID_REQUEST`, with `error.field` identifying the offending field |
| The identifiers are valid, but their effective intersection has zero executable inventory | `TARGETING_TOO_NARROW` |
| The effective target is executable, but outside the configured product's priced or guaranteed envelope | `REQUOTE_REQUIRED`; rediscover with the exact place overlay |

An accepted create confirms that the returned terms apply to the complete
effective targeting. `PLACE_TARGET_UNAVAILABLE` is reserved for later
degradation of a previously accepted, persisted place target; it is not a
create-time substitute for any result above.

### geo_places_exclude

- **Description**: Exclude catalog-backed named places
- **Format**: Same as `geo_places`
- **Example**: `[{ "country": "US", "system": "geonames", "place_type": "city", "values": ["5392171"], "value_labels": { "5392171": "San Jose, California, United States" } }]`
- **Note**: Seller must declare the system, exact country/type pair, and applied catalog version in `get_adcp_capabilities` and echo the persisted exclusion on package state.

### axe_include_segment
- **Description**: Segment ID for inclusion targeting (legacy AXE field)
- **Format**: String segment identifier
Expand Down Expand Up @@ -1146,7 +1200,7 @@ To remove all keyword targeting while preserving other overlay fields, send the

### Publishers MUST:

1. **Support Geographic Targeting**: Handle geographic inclusion and exclusion parameters (`geo_countries`, `geo_countries_exclude`, `geo_regions`, `geo_regions_exclude`, `geo_metros`, `geo_metros_exclude`, `geo_postal_areas`, `geo_postal_areas_exclude`) to the extent your platform supports them. Declare supported metro and postal systems in `get_adcp_capabilities`
1. **Support Geographic Targeting**: Handle geographic inclusion and exclusion parameters (`geo_countries`, `geo_countries_exclude`, `geo_regions`, `geo_regions_exclude`, `geo_metros`, `geo_metros_exclude`, `geo_postal_areas`, `geo_postal_areas_exclude`, `geo_places`, `geo_places_exclude`) to the extent your platform supports them. Declare supported metro, postal, and place systems in `get_adcp_capabilities`
2. **Interpret Briefs**: Use briefs to determine appropriate audience and content targeting
3. **Validate Targeting**: Reject media buys with targeting that cannot be supported
4. **Document Limitations**: Clearly communicate any geographic targeting limitations in product descriptions
Expand Down
12 changes: 12 additions & 0 deletions docs/media-buy/task-reference/create_media_buy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,17 @@ When executing a proposal, `proposal_status` on the returned proposal determines
| `start_time` | string | No | ISO 8601 date-time for this package's flight start. When omitted, inherits the media buy's `start_time`. Must fall within the media buy's date range. Does not support `"asap"`. |
| `end_time` | string | No | ISO 8601 date-time for this package's flight end. When omitted, inherits the media buy's `end_time`. Must fall within the media buy's date range. |
| `creative_assignments` | CreativeAssignment[] | No | Assign existing library creatives with optional weights and placement targeting |

For `targeting_overlay.geo_places` and `geo_places_exclude`, create-time values
must be within the selected Product's corresponding `overlay_support` tuple.
Unsupported dimensions, systems, countries, place types, or combinations return
`UNSUPPORTED_FEATURE`. A supported tuple with an invalid, unknown, or deprecated
ID—or an unsupported explicit catalog version—returns `INVALID_REQUEST` with
`error.field` on the offending field. Valid targeting with zero executable
inventory returns `TARGETING_TOO_NARROW`. Executable targeting outside the
configured product's priced or guaranteed envelope returns `REQUOTE_REQUIRED`,
after which the buyer rediscovers with the exact overlay. An accepted create
confirms the selected terms for the complete effective targeting.
| `creatives` | CreativeAsset[] | No | Upload new creative assets inline and assign. Requires `media_buy.features.inline_creative_management: true`; when the seller also advertises `creative.has_creative_library: true`, `creative_id` must not already exist in the library. |
| `context` | object | No | Opaque correlation data echoed unchanged in the package response, webhooks, and read surfaces. Use to map seller-assigned `package_id` back to your internal line items, campaign structure, or tracking state. Buyers targeting mixed seller populations SHOULD include a per-package correlation value here, commonly `context.buyer_ref`, for legacy sellers that do not echo `product_id`. |
| `measurement_terms` | [MeasurementTerms](/docs/media-buy/advanced-topics/pricing-models#measurement-terms-and-performance-standards) | No | Buyer's proposed billing measurement and makegood terms. Overrides product defaults. Seller accepts (echoed on confirmed package), rejects with `TERMS_REJECTED`, or adjusts. When omitted, product's `measurement_terms` apply. |
Expand Down Expand Up @@ -1064,6 +1075,7 @@ Common errors and resolutions:
| `UNSUPPORTED_FEATURE` | Canonical or legacy compatibility selector does not resolve or satisfy the product's closed accepted set | Re-author against the product's `format_options[]`, include required canonical parameters, or select a published `format_option_id`. |
| `BUDGET_TOO_LOW` | Budget below product minimum | Increase budget or choose different product |
| `TARGETING_TOO_NARROW` | Targeting yields zero inventory | Broaden geographic or audience criteria |
| `REQUOTE_REQUIRED` | A later package overlay is executable but falls outside the configured product's priced or guaranteed envelope | Re-run `get_products` with the exact targeting in `targeting_overlay`, then create from the returned configuration |
| `POLICY_VIOLATION` | Brand/product violates policy | Review publisher's content policies |
| `INVALID_PRICING_OPTION` | pricing_option_id not found | Use ID from product's `pricing_options` |
| `CREATIVE_LOCALE_NOT_ACCEPTED` | An assigned library creative has no materialized locale variant accepted by an in-scope format option, or its `serve_default` points outside that option's eligible set | Read `format_options[].locale_policy`, supply a matching source/target variant, narrow placement scope, choose another option, or change the default policy |
Expand Down
50 changes: 48 additions & 2 deletions docs/media-buy/task-reference/get_products.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,12 @@ or packages broken out by DMA or placement.

Request requirements and product support deliberately use different schemas.
The buyer request contains dimensions and required systems, never seller maxima.
A product value of `true` satisfies any protocol-valid requirement for that
dimension. When both sides use objects, every required boolean must be true in
A product value of `true`, where the dimension permits that form, satisfies any
protocol-valid requirement for that dimension. When both sides use objects, every required boolean must be true in
the product and every required array must be a subset of the product array.
Named places always use the object form: every requested identifier-system and country key must exist,
and the requested place types and catalog versions must be subsets of the
corresponding Product support arrays.
Missing or unknown requirements do not match. Product-only limits such as
`max_values_per_package` and `max_packages` are returned for planning after the
match.
Expand Down Expand Up @@ -354,6 +357,49 @@ Seller behavior is normative:

Audience evidence does not populate `targeting_overlay`, `demographic_targeting`, or an age-verification field. If the buyer also needs exact execution, it requests that independently through the corresponding product and package targeting surfaces.

Known place IDs belong in `targeting_overlay`, so the returned product's
availability, pricing, and forecast are scoped to the effective place
constraint. If the buyer will choose IDs later, `required_overlay_support`
asks for the country, identifier system, place type, and optional catalog
version that the product must let the buyer select:

```json
{
"$schema": "/schemas/media-buy/get-products-request.json",
"buying_mode": "brief",
"brief": "Local video inventory for a municipal services campaign",
"targeting_overlay": {
"geo_places": [{
"country": "NL",
"system": "geonames",
"system_version": "2026-05",
"place_type": "city",
"values": ["2759794"]
}]
},
"required_overlay_support": {
"geo_places": {
"systems": {
"geonames": {
"countries": { "NL": ["city"] },
"system_versions": ["2026-05"]
}
}
}
}
}
```

The returned Product `overlay_support.geo_places` is binding permission to
supply matching place IDs on packages later, subject to disclosed limits. It
does not guarantee value-specific inventory or preserve an earlier forecast or
price before the IDs are provided. If the eventual values fall outside the
configured product's priced or guaranteed envelope, the seller rejects create
with `REQUOTE_REQUIRED` and the buyer rediscovers with those exact values in
`targeting_overlay.geo_places`. Inclusion and exclusion permission are
independent; request `geo_places_exclude` separately when exclusions will be
chosen later.

### Placement fields

`get_products` returns product placement data when the seller includes `placements` or the buyer asks for it through `fields`. Placement IDs are publisher-scoped. Product placements should reference the publisher's public `adagents.json` placement declarations with `{publisher_domain, placement_id}` when a publisher declaration exists. Seller-private placement IDs, source/origin details, and delivery-system mappings must stay out of the response.
Expand Down
Loading
Loading