Type: spec amendment to #3305
Target: 3.1 (before GA — together with #4548)
Severity: load-bearing for adopter onboarding — PMax / Advantage+ / Amazon DSP are flagship products at their respective platforms; they can't be honestly represented in v2 today
Companion to: #4548 (order-logic scoping — separate axis)
Problem
Several flagship platform products are meta-products that fan out across multiple surfaces with per-surface policies:
- Google Performance Max (PMax) — one product, six surfaces (Search, Display, YouTube, Discover, Gmail, Maps). Each surface has different creative requirements, tracking models, targeting capabilities, brand safety controls. The advertiser doesn't choose surfaces; PMax ML decides.
- Meta Advantage+ — Facebook feed + Instagram feed + Reels + Stories + Marketplace + Audience Network. Similar fanout.
- Snap Public Profile suite — multiple surfaces with different formats per surface.
- TikTok Spark Ads — For You Page + Following feed + profile views.
- Amazon DSP — web display + OTT + mobile + Twitch as distinct surface types.
v2 today gives Product a channels: [\"display\", \"video\", \"social\"] array. That's too coarse — it says "this product serves on display channels" but doesn't capture:
- Per-surface creative requirements (PMax's YouTube surface needs
video_hosted; the Search surface needs text-only responsive_creative; the Display surface accepts image or html5 or responsive_creative)
- Per-surface tracking models (Search has CTR + position; Display has viewability; YouTube has VTR)
- Per-surface targeting capabilities (Search keyword targeting works on Search surfaces; doesn't apply on Display)
- Per-surface brand safety / inventory controls
Today a seller has two bad options: (a) declare a single Product with channels: [display, video, social] and lie about uniformity, OR (b) declare six separate Products and lose the "this is one logical product" framing buyers expect. Neither is honest.
Proposed change
Add Product.sub_products: SubProduct[] describing the surface-fanout structure.
{
\"product_id\": \"google_pmax\",
\"name\": \"Google Performance Max\",
\"sub_products\": [
{
\"sub_product_id\": \"pmax_search\",
\"surface\": \"google_search\",
\"channels\": [\"search\"],
\"format_options\": [{\"format_kind\": \"responsive_creative\", \"params\": {/* search-specific narrowing */}}],
\"targeting_capabilities\": {\"keyword_targeting\": {/* see #4548 */}},
\"reporting_capabilities\": {/* search-specific metrics */}
},
{
\"sub_product_id\": \"pmax_youtube\",
\"surface\": \"youtube\",
\"channels\": [\"olv\"],
\"format_options\": [{\"format_kind\": \"video_hosted\", \"params\": {/* youtube-specific */}}],
\"targeting_capabilities\": {\"audience_targeting\": {/* see #4548 */}},
\"reporting_capabilities\": {/* video-specific metrics */}
},
{
\"sub_product_id\": \"pmax_display\",
\"surface\": \"google_display\",
\"channels\": [\"display\"],
\"format_options\": [/* image, html5, responsive_creative */],
\"targeting_capabilities\": {/* display-specific */},
\"reporting_capabilities\": {/* display-specific */}
}
// ... discover, gmail, maps
]
}
Inheritance: a sub-product's fields override the parent Product's fields when present. Pricing, publisher properties, delivery type stay on the parent unless explicitly overridden per sub-product. Buyers reason about the parent Product for "is this a PMax buy?"; they reason about sub-products for "what creative does the YouTube surface accept?"
surface vocabulary: a new shared registry (/schemas/registries/surface-vocabulary.json, governance same as asset-group-vocabulary). Seed entries: google_search, google_display, youtube, google_discover, gmail, google_maps, facebook_feed, instagram_feed, instagram_reels, instagram_stories, meta_audience_network, snap_public_profile, tiktok_fyp, amazon_web_display, amazon_ott, amazon_twitch, etc.
Open questions
-
Sub-product fanout vs separate Products — Could we get the same expressiveness with multiple flat Products joined via umbrella_product_id: \"google_pmax\"? Pro: less new schema. Con: loses "buyer reasons about PMax as one logical thing"; reporting aggregation requires special-case logic.
-
Channels at parent vs sub-product — Should Product.channels still exist when sub_products is present? Lean: parent.channels = union(sub_products[*].channels), automatically derived. Buyers see consistent channel coverage either way.
-
Inheritance semantics — Should sub-product fields override parent fields or merge with them? Lean: override-when-present, inherit-when-absent. Same semantics as applies_to_channels on format_options.
-
Reporting aggregation — PMax reports aggregate by default but lets you drill down per surface. Does reporting_capabilities apply at the parent level (aggregate) or per sub-product (drill-down) or both? Probably both, with the parent's reporting_capabilities describing the aggregate view and each sub-product's describing the drill-down.
-
Interaction with applies_to_channels on format_options[i] — Is this redundant once sub_products exists? Lean: no — applies_to_channels is per-creative-option on a flat product (Flashtalking html5 OR internal display_tag on the same display surface); sub_products is per-surface structural fanout (PMax-shaped). Different axes.
Why this matters
PMax is Google's flagship buying product. Meta Advantage+ is Meta's. These can't ship in v2 without either lying about uniformity or fragmenting into six separate Products with no umbrella. Either choice undermines the "v2 is honest about real products" pitch. Adopters working on these integrations have already hit the gap; without a clean structural answer, they'll route around it via platform_extensions or by inventing seller-specific conventions.
Companion work
#4548 (sibling issue) addresses order logic — match types, audience signals, bid modifiers. The two RFCs share the targeting_capabilities / targeting field shape; sub-products in this RFC inherit and override the parent's targeting_capabilities from #4548. Both should land together so the PMax mapping is coherent end-to-end (one parent, six sub-products, each with its own creative envelope + targeting capability set).
Refs
- #3305 — v2 RFC
- #3307 — v2 implementation branch
- #4548 — order-logic scoping companion
Type: spec amendment to #3305
Target: 3.1 (before GA — together with #4548)
Severity: load-bearing for adopter onboarding — PMax / Advantage+ / Amazon DSP are flagship products at their respective platforms; they can't be honestly represented in v2 today
Companion to: #4548 (order-logic scoping — separate axis)
Problem
Several flagship platform products are meta-products that fan out across multiple surfaces with per-surface policies:
v2 today gives Product a
channels: [\"display\", \"video\", \"social\"]array. That's too coarse — it says "this product serves on display channels" but doesn't capture:video_hosted; the Search surface needs text-onlyresponsive_creative; the Display surface acceptsimageorhtml5orresponsive_creative)Today a seller has two bad options: (a) declare a single Product with
channels: [display, video, social]and lie about uniformity, OR (b) declare six separate Products and lose the "this is one logical product" framing buyers expect. Neither is honest.Proposed change
Add
Product.sub_products: SubProduct[]describing the surface-fanout structure.{ \"product_id\": \"google_pmax\", \"name\": \"Google Performance Max\", \"sub_products\": [ { \"sub_product_id\": \"pmax_search\", \"surface\": \"google_search\", \"channels\": [\"search\"], \"format_options\": [{\"format_kind\": \"responsive_creative\", \"params\": {/* search-specific narrowing */}}], \"targeting_capabilities\": {\"keyword_targeting\": {/* see #4548 */}}, \"reporting_capabilities\": {/* search-specific metrics */} }, { \"sub_product_id\": \"pmax_youtube\", \"surface\": \"youtube\", \"channels\": [\"olv\"], \"format_options\": [{\"format_kind\": \"video_hosted\", \"params\": {/* youtube-specific */}}], \"targeting_capabilities\": {\"audience_targeting\": {/* see #4548 */}}, \"reporting_capabilities\": {/* video-specific metrics */} }, { \"sub_product_id\": \"pmax_display\", \"surface\": \"google_display\", \"channels\": [\"display\"], \"format_options\": [/* image, html5, responsive_creative */], \"targeting_capabilities\": {/* display-specific */}, \"reporting_capabilities\": {/* display-specific */} } // ... discover, gmail, maps ] }Inheritance: a sub-product's fields override the parent Product's fields when present. Pricing, publisher properties, delivery type stay on the parent unless explicitly overridden per sub-product. Buyers reason about the parent Product for "is this a PMax buy?"; they reason about sub-products for "what creative does the YouTube surface accept?"
surfacevocabulary: a new shared registry (/schemas/registries/surface-vocabulary.json, governance same as asset-group-vocabulary). Seed entries:google_search,google_display,youtube,google_discover,gmail,google_maps,facebook_feed,instagram_feed,instagram_reels,instagram_stories,meta_audience_network,snap_public_profile,tiktok_fyp,amazon_web_display,amazon_ott,amazon_twitch, etc.Open questions
Sub-product fanout vs separate Products — Could we get the same expressiveness with multiple flat Products joined via
umbrella_product_id: \"google_pmax\"? Pro: less new schema. Con: loses "buyer reasons about PMax as one logical thing"; reporting aggregation requires special-case logic.Channels at parent vs sub-product — Should
Product.channelsstill exist whensub_productsis present? Lean: parent.channels =union(sub_products[*].channels), automatically derived. Buyers see consistent channel coverage either way.Inheritance semantics — Should sub-product fields override parent fields or merge with them? Lean: override-when-present, inherit-when-absent. Same semantics as
applies_to_channelson format_options.Reporting aggregation — PMax reports aggregate by default but lets you drill down per surface. Does
reporting_capabilitiesapply at the parent level (aggregate) or per sub-product (drill-down) or both? Probably both, with the parent'sreporting_capabilitiesdescribing the aggregate view and each sub-product's describing the drill-down.Interaction with
applies_to_channelsonformat_options[i]— Is this redundant once sub_products exists? Lean: no —applies_to_channelsis per-creative-option on a flat product (Flashtalking html5 OR internal display_tag on the same display surface);sub_productsis per-surface structural fanout (PMax-shaped). Different axes.Why this matters
PMax is Google's flagship buying product. Meta Advantage+ is Meta's. These can't ship in v2 without either lying about uniformity or fragmenting into six separate Products with no umbrella. Either choice undermines the "v2 is honest about real products" pitch. Adopters working on these integrations have already hit the gap; without a clean structural answer, they'll route around it via
platform_extensionsor by inventing seller-specific conventions.Companion work
#4548 (sibling issue) addresses order logic — match types, audience signals, bid modifiers. The two RFCs share the
targeting_capabilities/targetingfield shape; sub-products in this RFC inherit and override the parent'stargeting_capabilitiesfrom #4548. Both should land together so the PMax mapping is coherent end-to-end (one parent, six sub-products, each with its own creative envelope + targeting capability set).Refs