Skip to main content

Overview

You can attach structured targeting intent to a campaign when creating or updating it through the V3 MCP surface (save_campaign on /mcp/v3). Targeting records what the campaign intends to reach — geography, language, device type, daypart, and demographic dimensions — and persists that intent with the campaign object.
Campaign targeting is stored and returned on the campaign workspace. When you create a media buy with save_media_buy, its targeting can narrow the V3 campaign overlay but can never widen it.

Passing targeting to save_campaign

The targeting parameter is optional. Omit it to leave targeting unset; include it to attach one or more dimensions:
Each dimension is an array of requirement entries. A requirement entry has:

Supported dimensions

Reading targeting back

After save, targetingOverlay appears on the campaign workspace returned by save_campaign and by get(kind: "campaign"). Existing campaigns with no targeting set omit the field entirely. The deprecated targeting field keeps its existing behavior: it is returned verbatim only when the campaign has stored legacy targeting.

Updating targeting

Pass targeting in a save_campaign update to replace the stored targeting intent. Partial dimension updates replace the entire targeting object — each call is the full desired state, not a patch.

Targeting stated in the brief

The campaign brief reaches every seller in a proposal round. Sellers read it to understand the campaign and to choose what to recommend, for example inventory suited to an age group or an age signal they sell. A hard requirement stated only in the brief, such as “please target women 18-55”, is not applied the same way by every seller:
  • Storefront-built and Agent-supplied storefronts read it from the brief and never refuse a request only because the brief states targeting. Whether it restricts delivery or is used only to recommend products depends on the AdCP version of the request; see Exact Age Targeting.
  • Built-in platform adapters read one closed age range from the brief.
  • Other sellers interpret the brief with their own sales agent.
AdCP asks buyers to put every requirement that has a structured field into that field, so the requirement does not depend on each seller reading prose. Set hard targeting in the campaign’s targetingOverlay and keep the brief as written. The brief still goes to every seller as context.
  • targetingOverlay replaces the campaign’s stored overlay. Send the fields already set, such as the campaign’s countries, together with the new one.
  • Only move a requirement that has a field. A requirement with no structured field, such as gender, stays in the brief only.
  • A preference (“ideally 18-55”) or a description of the audience (“our customers are mostly in their 30s”) stays in the brief. A structured field is applied as a restriction wherever the seller supports it.
  • Keep the brief and the structured fields consistent. AdCP 3.2 requires a seller to refuse a request whose brief contradicts its structured targeting.
Structured targeting changes what you get back. Proposal rounds send the campaign’s age range to storefronts that read it, and those storefronts return only products that can deliver that range. At launch, a campaign age range reaches only the sellers that can apply it; see Campaign age range at launch.

Campaign targeting and media buys

save_media_buy applies the campaign targeting overlay as a guardrail to each product’s targetingOverlay. An explicit product targetingOverlay supplies that buy’s targeting, and campaign targeting still narrows it: overlapping values are kept, and a product value outside the campaign returns CONFLICT. A product can omit a campaign field to inherit it, or narrow it where the field has defined containment rules. It cannot widen the campaign audience. signal_targeting is excluded from the overlay because it is a priced, product-scoped selection that will come through the signals path. Typed signal_targeting_groups remains supported. On a V3 media-buy product overlay: Deprecated. Numeric codes replace names. Removal will be announced in release notes at least 14 days ahead; planned for 2 November 2026 (AI-10440). Codes must be in the Nielsen DMA dictionary; unknown codes return nearby code candidates. For country, region, metro, postal, language, browser, device type, and device platform includes, the buy uses the overlap. Excludes are combined. A buy’s demographic age range is intersected, unknown ages are included only when both overlays allow them, and accepted age bases are intersected. The stricter age restriction wins. Each buy daypart must fit wholly inside a campaign daypart on the same clock; dayparts are never clipped or converted between timezones. For proximity, named places, and other structured values without a containment rule, a product must omit the field or repeat the campaign value exactly. If the included values for the same field do not overlap, save_media_buy returns a CONFLICT error. The error names the field and includes the campaign and product values so you can correct one of them. It does not create a media buy that targets everywhere. Omit a product geography field to inherit the campaign’s value for that field. Language, device, daypart, and demographic campaign intent now participate in this guardrail. A demographic overlay still cannot dispatch while seller transport is on AdCP 3.1, so save_media_buy rejects it before seller contact.

Postcodes on live media buys

Saving targetingOverlay.geo_postal_areas on a campaign also sends the postcodes to the campaign’s live media buys. Give every postal area its country (or a country-specific system such as au_postcode): some sellers look a postcode up by its bare code, which can exist in several countries. Campaign postcodes replace a live buy’s location targeting. A country, region, or Nielsen DMA the same save does not set is removed from the live buy, so changing a campaign from regions to postcodes leaves only the postcodes. Media buys that have ended (completed, canceled, failed, or rejected) are left as they are. Removing postcodes from the campaign removes them from the live buy when the buy still targets a country, region, or DMA afterwards, either one it already had or one the same save sets. A save that would leave a live buy with no location targeting is refused, because the buy would otherwise target every location. Postcodes set through the V2 constraints field are stored on the campaign but not sent to live buys. Seller support varies. See each platform’s page under Ad platform compatibility; for example, OpenAI Ads refuses a postcode together with the country or region that contains it, and does not support postcode exclusion.

Campaign age range at launch

A campaign’s age range (demographics.age) is copied into every package when the campaign launches. A media buy on a Meta or Spotify storefront that approves media buys automatically is sent under AdCP 3.2, and the seller applies the age range. Every other seller cannot yet apply a campaign age range at launch. That includes the other built-in platform adapters, a Meta or Spotify storefront that reviews media buys manually, Storefront-built storefronts and external AdCP sellers. Their packages are created without the age range, so the media buy launches and runs without age targeting. The launch response says so with a demographics_not_applied warning that names each affected product and package buyerRef. A product can carry its own fixed age audience. It counts only when the product delivers that age to every impression, and when the age is an AdCP included signal, only when the signal states how the ages are known in a way the campaign accepts: verified or self-declared ages, plus modeled ages when the campaign allows modeled age. A survey-based or modeled audience claim is treated as no fixed audience (see Which products match a requested range):
  • Fixed to exactly the campaign’s range: no warning; the product delivers it.
  • Fixed to a range inside the campaign’s, such as 18-34 for an 18-55 campaign: the package is bought with the product’s range, on every seller. The launch response confirms what was sent with a demographics_narrowed receipt naming the product and both ranges. That covers a product selected by id or a campaign range changed after you reviewed proposals.
  • Fixed to a range that reaches ages outside the campaign’s, such as 18-64 for a 25-54 campaign: that media buy is refused unless its seller can apply the campaign’s range, because it would deliver to people the campaign excludes.

Campaign audiences and catalogs

save_campaign also accepts audienceConfig and catalogId. Use audienceConfig.targetAudienceIds to include up to 100 audience IDs and audienceConfig.suppressAudienceIds to exclude up to 100 audience IDs. On an update, deleteMissing: true replaces both lists; otherwise, supplied IDs are added to the existing lists. catalogId is the public catalog ID returned by the catalog read surfaces; pass null on an update to remove it. Both fields are stored and returned by get(kind: "campaign"). Audiences. A campaign with audiences launches when every seller on it takes mapped audiences — Meta and OpenAI Ads today. Map the audience to the seller account first with save_audience and check it reads synced; see Map event sources and audiences. A seller that does not take mapped audiences still blocks the launch, and the campaign readiness response names that seller. Catalogs. A campaign with a catalog launches when every live line sells through a product that declares it takes that catalog’s type. Otherwise the readiness response returns catalog_sync_unavailable. Remove the catalog to launch without it.

Targeting each product offers

Each product says which targeting you may set on a package for it: its AdCP overlay_support, such as countries, regions, Nielsen DMA metros or a frequency cap, with any per-package limits the seller sets. You can book only what a product offers. Where you see it:
  • get_products returns overlay_support on each product that declares targeting, in both wholesale and brief results. A product composed from inventory carries the declaration of the inventory product it is built from. See The targeting a product lets you set.
  • V3 search(kind: "wholesale_product") returns overlaySupport and supportedTargeting on each product that declares targeting. A declaration too large to return inline is left out of search, leaving supportedTargeting.
  • V3 get(kind: "wholesale_product") returns one product, by the productId and advertiserId from your search, with its complete overlaySupport.
  • V3 get(kind: "seller") returns the seller’s own targeting (targeting and supportedTargeting). It applies to the seller’s products that declare none of their own.
When you save a draft media buy, create one, or update its targeting, each package’s targeting is checked against its product’s declaration. The targeting checked is what the buy will send the seller, including campaign targeting the package inherits. A package is refused when it targets a dimension the product doesn’t declare, a metro or postal system it doesn’t list, or more values than a per-package limit allows. Nothing is saved or sent to the seller when a package is refused.
  • Through Apostra’s buyer tools (save_media_buy, save_campaign, create_media_buys) the refusal is a validation error on targeting_overlay, for example: “The selected products do not offer the requested targeting: package line-1 targets what product … does not offer: geo_metros system uk_itl1 is not offered by this product (offered: nielsen_dma).”
  • Through a storefront’s AdCP endpoint the refusal is UNSUPPORTED_FEATURE with details.reason undeclared_product_targeting and the same message.
To fix it, remove the named targeting from the package, or from the campaign when the package inherits it, or choose a product that offers it. Gender targeting (demographics.gender, added in AdCP 3.3) is checked the same way. A product offers it only by declaring overlay_support.demographics.gender; demographics: true or an age-only declaration does not cover it. No product declares gender yet, so a package that targets gender is refused, with demographics.gender is not offered by this product, rather than sent to a seller that would ignore it. A product on a seller’s managed sales agent that the seller hasn’t narrowed offers the targeting the seller’s ad server can apply, and is checked against that in the same way. A product on any other source that declares no targeting of its own isn’t checked this way. The seller’s own targeting applies to it instead, and a booking that asks for targeting the seller can’t apply is refused.

Rollout and release check

This is available immediately on the public V3 save_campaign and get(kind: "campaign") surfaces; it has no separate customer enrollment or feature flag. The Buyer V3 API release owner verifies the release through the automated PR Acceptance Tests catalog contract proof, then confirms in the released environment that a campaign can save and read back an audience or catalog and that a launch attempt returns the seller-sync blocker. The release check passes only when all three behaviors agree: save persists the requested IDs, get returns them, and launch stays refused while seller sync is unavailable. Treat a missing readback value, a launch that succeeds, or a blocker other than the seller-sync refusal as a mitigation trigger. Roll back the release or remove the audience/catalog from the affected campaign; do not override the blocker or treat the objects as delivered.

Backward compatibility

targeting is deprecated, but its behavior is unchanged. targetingOverlay is the new V3 targeting field. Its removal is a separately announced change (AI-10440) with at least 14 days’ notice. Existing calls and campaigns are not affected.