Skip to content

RFC: Creative Formats v2 — canonical formats live on products, composition primitives, validate_input #3305

@bokelley

Description

@bokelley

RFC: Creative Formats v2 — canonical formats with tracking baked in, product-bound variants, validate_input

Type: RFC / major additive feature
Severity: high (sets the direction for creative format authoring across 3.x)
Target: 3.1 preview track; stabilize through 3.2/3.3; v1 named-format model deprecation continues into 4.x with full removal at 5.0

Supersedes: #3269 (canonical asset_id slot vocabulary), #3270 (output-validation handshake)
Adjacent: #3268 (PreviewCreativeResponse single-render hoist) — separate but coordinated, ships 3.1
First implementation: #3307 (Phase 1 + Phase 2 preview branch)

Revision history:

  • r2 (2026-04-26): Incorporated 3-expert-review feedback (synthesis): canonical IS the contract; sponsored_placement split 3-way; nondeterminism gap explicit; digest pinning; build_creative typed inputs.
  • r3 (2026-04-26): Working-example walkthrough refinements: tracking baked into each canonical format (not separated); split image / html5 / display_tag and video_hosted / video_vast and audio_hosted / audio_daast because tracking models are fundamentally different per format; format keyed by canonical name (drop discriminator); platform extensions distributed via URI+digest bundled in get_products.
  • r4 (2026-04-27): Three more simplifications from end-to-end walkthrough.
    • build_capability and build_capability_ref dissolved into inputs directly on the format declaration. External creative agents are invisible to the buyer in the typical case (sales agent is typically the creative agent, or seller calls upstream agent internally — buyer-seller boundary is the only protocol relationship).
    • creative.creative_build_capabilities renamed to creative.supported_formats on get_adcp_capabilities. Drops "build_" framing on the discovery surface; uses ProductFormatDeclaration shape (one primitive, two homes — sales-side inline on products, creative-agent-side as supported_formats list).
    • asset_group_id field added to format.json slot declarations as a v1↔v2 migration bridge. Lets v1 reference creatives declare their canonical equivalents inline (e.g., slot click_urlasset_group_id: "landing_page_url").
    • Plus: vocabulary aliases expanded for common slots (audit-grounded); Phase 1 zip asset type replaced the earlier delivery_type addition on html/javascript (URL-delivered HTML/JS routes through url-asset; HTML5 zip bundles get a proper first-class asset type).
    • Sales agents do NOT expose build_creative — that's a creative-agent surface only. Creative agents may also expose sync_creatives (for ad server use cases). Both roles provide preview_creative. The two flows for inputs-driven creative: Flow 1 — buyer pre-produces via a creative agent's build_creative and submits the rendered manifest via sync_creatives to the seller. Flow 2 — buyer submits inputs directly via sync_creatives and the seller produces internally.

Problem

Today's creative format model has friction adopters hit on day one, plus an architectural drift that produced most of that friction:

  1. No explicit "format master" role. Formats are entities in a registry separate from products. Sellers maintain platform-specific format names (meta_reels, tiktok_in_feed_video, spotify_audio_standard) that are really product-variant declarations. The split forces sellers to author and maintain duplicate metadata and obscures what's universal vs platform-specific.

  2. Format asset_id slots are author-invented strings. Every adopter picks script or body or text for "the spoken script body of an audio ad." Asset-group canonical vocabulary exists for catalogs but isn't reused at the format slot level.

  3. format_id is a compound { agent_url, id } object where the name promises a string. Can't be used as a map key. SDK codegen exposes object lookups instead of typed accessors. No content-digest pinning means third-party agents can mutate format definitions mid-flight.

  4. No protocol-level handshake for input validation. Creative-template platforms whose output isn't deterministically format-compliant from inputs alone have no clean rejection path.

  5. PreviewCreativeResponse requires four levels of nesting for the single-render case (Simplify PreviewCreativeResponse for the single-render case #3268).

  6. Generative formats proliferate. Every social/search/UA platform has 3-7 *_generated_* formats today. Most are structurally creative_brief + <reference shape>.

  7. promoted_offerings formats duplicated across 8 of 12 platforms with identical internal creative shape.

  8. Pure-catalog and AI-surface formats (Amazon SP, Pinterest Collection, Google PMax, ChatGPT-style brand mentions) have no buyer creative slots — they're surface-composed. Modeling as formats with empty creative shapes is awkward; lumping them into one cluster also wrong.

  9. Six different field names for "destination URL" today: click_url, link, final_url, link_url, landing_page_url, click_through_url.

  10. Tracking is mistakenly treated as cross-cutting platform metadata. It's actually format-specific: VAST tracking is the VAST spec; HTML5 tracking is MRAID + OM-SDK; image tracking is impression pixel + click URL. These don't vary platform to platform — they vary format to format. Lumping image | html | javascript under one canonical hides this; treating tracking as a platform-level extension creates needless indirection.

Empirical input

Three audits drove this RFC:

  • Social/retail platform formats: 12 platforms, 86 ad formats. ~76% fit a small canonical format set; remaining ~24% platform-specific (catalog-driven retail media, dynamic profile-data, multi-page story, interactive quiz, lead-form, message/InMail, sponsored chat).
  • Tag handling in social/retail: zero hits across all 12 social/retail adapters for vast, daast, html5, iframe, display_tag. Walled-garden hosted assets only.
  • Tag handling in publisher-direct (GAM via prebid salesagent): maps to ThirdPartyCreative, CustomCreative, ImageRedirectCreative/VideoRedirectCreative, TemplateCreative (native). Wire format already accepts AdCP delivery_type: url|inline.

Adopter signal: AudioStack @adcp/client v6.0 be-Emma test surfaced #3268, #3269, #3270 in a single round.

Goals

  1. AdCP defines canonical formats; sellers' products narrow them. Canonical format is the buyer's stable contract. Products narrow it via inline ProductFormatDeclaration carrying parameters and platform extensions; they don't replace it.
  2. Tracking and measurement are baked into each canonical format, not abstracted into platform extensions. A canonical's tracking events, macro substitutions, click handling, and measurement integrations are part of its schema.
  3. Format slot names draw from a canonical, governed vocabulary (asset_group_id).
  4. format_id is a string; canonical formats have short canonical names. URI form (creative-agent capabilities and third-party formats) is content-digest-pinned.
  5. Surface a clean input-validation primitive (validate_input), with explicit handling for nondeterministic generative platforms.
  6. Generative formats are build_creative capabilities targeting canonical formats. Inputs are a typed vocabulary.
  7. SDK codegen of canonical formats and canonical patterns; platform extensions distributed via URI+digest bundled in get_products.
  8. Brand identity is implicit context via brand.json, with brand_kit_override for the missing/stale case.

Non-goals

  • Forcing named-format dissolution. v2 product-bound declarations are opt-in. v1 named formats remain a first-class path indefinitely.
  • Designing native canonical format without first auditing TemplateCreative + OpenRTB Native 1.2 — see Phase gating.
  • Designing the AI mediation protocol (third part of SI's decomposition — see §2).
  • output_modality declaration (deferred until text/audio AI-surface formats need it).

Schema strategy

v2 ships side-by-side with v1 in the existing v3 namespace.

v1-compatible primitives shared with v2: asset primitives (image-asset, video-asset, audio-asset, vast-asset, daast-asset), catalog/offering schemas, manifest envelope (creative-manifest.json), response envelopes.

Modified backwards-compatibly: html-asset.json and javascript-asset.json add optional delivery_type: "url" | "inline" (✅ #3307). product.json adds optional format field carrying v2 ProductFormatDeclaration.

Net-new v2 schemas: canonical format definitions (11) + _base.json with shared parameters including inputs, product-format-declaration.json, asset-group-vocabulary.json (✅), scenes.json (✅), zip-asset.json (✅), platform-extension-ref.json, validate-input-{request,response,result}.json. Note: build-capability.json, build-capability-ref.json, list-build-capabilities-*.json from earlier revisions were dropped in r4 — input contract folds into the format declaration; creative-agent capability discovery folds into creative.supported_formats on get_adcp_capabilities. Canonical-pattern schemas for cta_vocabulary / destination_kinds / brand_safety / universal_macros were dropped earlier (vocabularies stay where they already live; tracking is baked into formats).

Version discrimination: A given product is either v1 (references a separately-defined format by format_id) or v2 (carries its format declaration inline). Sellers can publish both during transition.

Design

1. Architectural framing — three layers

Canonical formats (11) — AdCP-defined universal building blocks. The canonical format itself is the buyer's contract. Same as OpenRTB and VAST treat creative format: a stable reference buyers validate against before knowing which seller wins. Each canonical bakes in its tracking events, macro substitutions, click handling, measurement integrations — because tracking is format-specific, not platform-specific.

Canonical patterns — cross-format shared concerns published by AAO, compiled into SDKs. Smaller set than I originally drafted: cta_vocabulary, destination_kinds, brand_safety_frameworks, universal_macros. Tracking is not a canonical pattern — it lives in the canonical format that owns it.

Platform extensions — narrow, truly platform-specific additions. Pixel ID formats (Meta vs TikTok vs Google), conversion event taxonomies, platform-specific CTA additions (Meta WATCH_MORE), platform-specific destinations (Meta messenger_thread, whatsapp_phone). Distributed via URI+digest, bundled in get_products responses (see §5c).

2. The 11 canonical formats

Split by tracking/measurement model (because tracking is fundamentally different per format) plus output shape.

Canonical What it is Tracking model
image Static image (jpg/png/gif/webp), file upload or hosted URL redirect. AR/dimensions parameters cover IAB sizes. Impression pixel + click URL + optional viewability pixel
html5 Interactive HTML5 banner (zip URL or inline). MRAID + OM-SDK + click-tag macro + backup image
display_tag Third-party JS/iframe tag URL Opaque to seller (third-party serves and measures)
image_carousel Multi-card (per-card image OR video, polymorphic) Per-card pixels + carousel-level engagement
video_hosted Direct video file (mp4/webm/mov), orientation parameter (vertical 9:16 / horizontal 16:9 / square 1:1) OM-SDK + external impression/click/quartile trackers
video_vast VAST tag (URL or inline XML), VAST 2-4.x, orientation parameter Inherent VAST events (impression, quartiles, click, complete, error, mute, expand)
audio_hosted Direct audio file (mp3/aac/wav) Impression + completion + companion-image-pixel
audio_daast DAAST tag Inherent DAAST events
sponsored_placement Retail-media catalog-driven, surface composes Per-item impression + click + conversion (catalog-keyed)
responsive_creative Buyer asset pool, surface composes combinations (Google Responsive Display/Search Ads, Performance Max, Demand Gen; Meta Advantage+ creative). Industry term: "Responsive" (Google) / "Advantage+ creative" (Meta). Per-asset performance breakdown by surface
agent_placement Sponsored placement composed by an AI surface in response to a user query (ChatGPT, Perplexity, voice assistants, sponsored search snippets). Distinct from si_chat (brand-owned conversation; user → brand's agent — different protocol layer). Parallels sponsored_placement structurally; both surface-composed, differ by surface type. Mention-level impression + attribution

Each canonical format's schema declares:

  • Slot vocabulary drawn from canonical asset_group_id registry (or format-specific where role doesn't generalize)
  • Parameter shape (dimensions, duration, file size, codec, character limits)
  • Tracking events the format emits and their substitution patterns (referencing universal_macros)
  • Click handling and destination resolution (referencing destination_kinds)
  • Measurement integrations the format supports (OM-SDK, MRAID, VAST events, etc.)
  • composition_model: deterministic | algorithmic — explicit field; sponsored_placement is deterministic, responsive_creative and agent_placement are algorithmic
  • provenance_required?: boolean — optional flag for brand-safety-sensitive products to refuse unsigned synthesized assets (C2PA hooks)

Sponsored placement and the SI architectural decomposition

The three sponsored canonicals (sponsored_placement, responsive_creative, agent_placement) cover part 1 of three architecturally distinct patterns currently conflated under "Sponsored Intelligence":

  1. Sponsored placement (these canonicals) — surface composes from buyer's catalog/asset-pool/brand data. Buyer supplies inputs; surface owns rendering.
  2. Chat with advertiser — user converses directly with the brand's SI agent. Out of scope; existing SI agent track.
  3. AI mediation — surface (ChatGPT, Perplexity) discovers/selects/routes between advertisers. Out of scope; needs its own RFC track.

v2 ships #1 inside the canonical format catalog. The other two stay on their respective tracks.

3. Product format declarations — keyed by canonical name

A v2 product carries its format declaration inline, keyed by canonical format name:

interface Product {
  product_id: string;
  // ... existing product fields ...
  format?: {
    [CanonicalFormatId]: ProductFormatParameters & {
      cta?: CtaPatternRef;
      destinations?: DestinationKindsRef;
      brand_safety?: BrandSafetyFrameworkRef;
      tracking?: TrackingExtensions;
      composition_model?: 'deterministic' | 'algorithmic';
      provenance_required?: boolean;
      platform_extensions?: ExtensionRef[];
      inputs?: InputsContract;            // r4: what the format requires from the buyer; lives directly on the format
      production_window_business_days?: number;  // r4: production turnaround when format requires agent production
    };
  };
  // r4: build_capability_ref dropped — external creative agent relationships are invisible to the buyer
}

The format key (image, video_vast, audio_hosted, etc.) IS the format declaration. SDKs codegen CanonicalFormats.image({...}) accessors matching exactly. No canonical discriminator field needed — the key is the discriminator.

A product can carry exactly one format key; validators enforce this.

Worked example — Meta Reels (video_hosted narrowed)

{
  "product_id": "meta_reels_us",
  "format": {
    "video_hosted": {
      "orientation": "vertical",
      "duration_ms_range": [3000, 90000],
      "aspect_ratio": "9:16",
      "min_width": 1080,
      "min_height": 1920,
      "max_file_size_mb": 200,
      "video_codecs": ["h264"],
      "audio_codecs": ["aac"],
      "headline_max_chars": 25,
      "primary_text_max_chars": 72,
      "captions": "recommended",
      "cta": {
        "canonical_set": "cta_vocabulary",
        "values": ["LEARN_MORE", "SHOP_NOW", "DOWNLOAD", "SIGN_UP"],
        "extensions": ["meta_cta_watch_more"]
      },
      "destinations": {
        "canonical_set": "destination_kinds",
        "values": ["web", "app_install", "deep_link"],
        "extensions": ["meta_destinations_messenger", "meta_destinations_whatsapp"]
      },
      "brand_safety": "iab_content_taxonomy_3",
      "platform_extensions": [
        { "uri": "https://meta.adcp/extensions/meta_pixel", "digest": "sha256:..." },
        { "uri": "https://meta.adcp/extensions/meta_placements_reels", "digest": "sha256:..." }
      ]
    }
  }
}

The video_hosted canonical format already declares OM-SDK measurement, external impression/click/quartile trackers, and macro substitution patterns ({CACHEBUSTER}, {CLICK_URL}, {OFFERING_ID} etc. from universal_macros). Meta's product narrows duration, dimensions, slot character limits, and references its specific extensions for pixel ID and Reels placement. The buyer sees "video_hosted with these constraints + Meta's extensions" rather than a sea of separately-declared concerns.

Worked example — IAB MREC (image narrowed, multi-seller standard)

{
  "product_id": "nytimes_homepage_mrec",
  "format": {
    "image": {
      "width": 300,
      "height": 250,
      "max_file_size_kb": 200,
      "image_formats": ["jpg", "png", "gif"],
      "ssl_required": true,
      "cta": {
        "canonical_set": "cta_vocabulary",
        "values": ["LEARN_MORE", "SHOP_NOW", "GET_OFFER"]
      },
      "destinations": {
        "canonical_set": "destination_kinds",
        "values": ["web"]
      },
      "brand_safety": "iab_content_taxonomy_3",
      "platform_extensions": [
        { "uri": "https://nytimes.adcp/extensions/nytimes_om_strict", "digest": "sha256:..." }
      ]
    }
  }
}

The image canonical format already declares impression pixel + click URL + optional viewability pixel; NYTimes's product narrows the dimensions to MREC, file size, image formats, and references its strict OM extension. Buyers can validate a manifest against canonical image BEFORE knowing which publisher wins — every IAB MREC product across publishers narrows the same canonical with similar parameters. That's the canonical-as-contract value.

For HTML5 banners on the same placement, the seller publishes a separate product:

{
  "product_id": "nytimes_homepage_html5",
  "format": {
    "html5": {
      "width": 300,
      "height": 250,
      "max_initial_load_kb": 200,
      "max_polite_load_kb": 500,
      "host_initiated_subload": true,
      "max_animation_duration_ms": 30000,
      "max_cpu_load": 30,
      "mraid_required": true,
      "om_sdk_required": true,
      "clicktag_macro": "clickTag",
      "backup_image_required": true,
      "backup_image_max_size_kb": 50
    }
  }
}

The html5 canonical format declares MRAID + OM-SDK + click-tag macro + backup image as part of its tracking spec. NYTimes narrows the dimensions, file size limits, and animation constraints. Different format from image because the tracking model is fundamentally different.

Worked example — Podcast 30s host-read (audio_hosted with inline inputs)

Host-reads are the host-recorded-from-buyer-script pattern. The buyer doesn't ship audio — they supply a script and the publisher's host records the audio. The format declaration carries the input contract directly; no separate capability lookup needed.

{
  "product_id": "the_daily_30s_host_read_us",
  "format": {
    "audio_hosted": {
      "duration_ms_exact": 30000,
      "audio_codecs": ["mp3", "aac"],
      "audio_sample_rates": [44100, 48000],
      "audio_channels": ["stereo"],
      "loudness_lufs": -16,
      "buyer_audio_acceptance": "rejected",
      "audio_source": "publisher_host_recorded",
      "tracking": {
        "extensions": ["podcast_dynamic_insertion_macros"]
      },
      "inputs": {
        "script": {
          "required": true,
          "max_chars": 800,
          "description": "Verbatim script the host will read. Exact wording — no improvisation; legal pre-cleared."
        },
        "brand": { "required": true },
        "offering_ref": { "required": false }
      },
      "production_window_business_days": 7
    }
  }
}

Two flows depending on whether the buyer pre-produces or the seller produces internally:

  • Flow 1 — buyer pre-produces. Buyer reads the format → calls build_creative({ format: <The Daily's audio_hosted narrowing>, inputs: { script, brand } }) on a creative agent (could be The Daily's own creative-agent surface, the buyer's in-house studio, AudioStack-style services — any agent that lists this format under creative.supported_formats on get_adcp_capabilities) → receives rendered manifest → submits via sync_creatives to The Daily's sales agent.
  • Flow 2 — seller produces internally. Buyer reads the same format → submits via sync_creatives with the inputs directly → seller produces internally (host records, calls upstream creative agent, whatever) → returns async status; buyer polls or waits for completion.

The format's audio_source and buyer_audio_acceptance parameters tell the buyer which flows are accepted. Both flows are valid for The Daily's host-read because the publisher's host MUST be the producer in either case — the difference is whether the buyer drives the build call or the seller drives it. Other products might accept Flow 1 only or Flow 2 only.

Verbatim host-reads use script; talking-points host-reads use creative_brief. Same target format; different input contract. The format itself encodes which is needed.

Sales agents do not expose build_creative

build_creative is a creative-agent surface only. Sales agents expose get_products and sync_creatives. Creative agents may also expose sync_creatives for the ad server use case (an agent can act as both a creative agent for build flows and an ad server for delivery). Both roles provide preview_creative. The creative.supported_formats field on get_adcp_capabilities is what creative agents declare; it uses the same ProductFormatDeclaration shape as products' inline format.

4. format_id as URI string with content-digest pinning

v2 format_id is a string. Three valid forms:

  • Canonical reference: format_id: "video_hosted" — bare token, resolves to AAO canonical.
  • Product reference: product_id is the seller-side identity; the product's format declaration is the source of truth for that variant.
  • URI form for creative-agent capabilities and third-party formats: format_id: "https://creative.audiostack.com/adcp#audio_30s". Pinned via content digest.

Content-digest pinning (required for URI form): Buyers SHOULD include format_digest: "sha256:...". Agents MUST publish digests. Mid-flight format-definition mutation breaks the digest match; buyers detect drift before validating against a stale shape. Same model as TMP schema-pinning; addresses #2335.

For canonical AAO formats, SDKs ship the catalog with versioned digests at well-known paths.

5. Canonical asset_group_id vocabulary (✅ shipped in #3307)

Format slot names draw from a governed vocabulary. ✅ Shipped: 7 catalog vocab entries + 12 audit-driven additions in static/schemas/source/core/asset-group-vocabulary.json. Includes landing_page_url canonicalizing 6 v1 alias names.

Build-time inputs vocabulary for build_creative:

ID Used by
creative_brief Text-driven generative (talking points, brief style)
script TTS, video voiceover, host-read podcast (verbatim wording)
scenes Generative video — typed scene-by-scene structure
voice_id TTS — voice selection from format-defined enum
style_reference Image-to-image, style transfer (image asset reference)
starter_assets Reference assets for variation/transformation
product_feed Catalog-driven generative

Scenes schema (✅ shipped at /schemas/v3/creative/scenes.json).

5b. Canonical patterns (smaller layer)

Cross-format shared concerns. AAO-published, SDK-compiled. Each is a versioned JSON file with content digest.

Pattern Purpose Used by
cta_vocabulary Universal CTA enum (LEARN_MORE, SHOP_NOW, DOWNLOAD, SIGN_UP, GET_OFFER, BOOK_NOW, CONTACT_US, APPLY_NOW, WATCH_VIDEO) Most canonical formats
destination_kinds URL kind taxonomy (web, app_install, deep_link, dial_phone, sms) Most canonical formats
brand_safety_frameworks IAB Content Taxonomy 3, GARM brand safety floor, age-gate semantics All canonical formats
universal_macros {CACHEBUSTER}, {CLICK_URL}, {OFFERING_ID}, {MEDIA_BUY_ID}, {CREATIVE_ID} etc. Tracking endpoints across all formats

Tracking is not a canonical pattern. Each canonical format declares its own tracking events; they reference universal_macros for substitution. That's the only cross-format tracking concept.

5c. Platform extensions — distribution and discovery

Platform extensions are narrow, truly platform-specific additions to canonical patterns or canonical format slots. Examples:

  • meta_pixel — extends tracking slots on most formats: adds pixel_id, conversion_event enum
  • meta_destinations_messenger — extends destination_kinds: adds messenger_thread_id
  • meta_cta_watch_more — extends cta_vocabulary: adds WATCH_MORE for Reels
  • tiktok_pixel — extends tracking: adds TikTok-specific pixel field shape
  • nytimes_om_strict — extends tracking: adds NYTimes' strict OM-SDK requirements

Where they live: at well-known paths on the owning agent.

https://meta.adcp/extensions/meta_pixel
https://tiktok.adcp/extensions/tiktok_pixel
https://nytimes.adcp/extensions/nytimes_om_strict

Each extension's response carries: extension schema (which fields it adds), the canonical pattern or slot it extends, version, and content digest.

How buyers get them: bundled in get_products. The sales agent's response includes definitions for any extension referenced by any product in the response:

{
  "products": [
    {
      "product_id": "meta_reels_us",
      "format": {
        "video_hosted": {
          ...
          "platform_extensions": [
            { "uri": "https://meta.adcp/extensions/meta_pixel", "digest": "sha256:a3f5..." }
          ]
        }
      }
    }
  ],
  "extensions": {
    "https://meta.adcp/extensions/meta_pixel@sha256:a3f5...": {
      "extends": "tracking",
      "fields": {
        "pixel_id": { "type": "string", "required": true },
        "conversion_event": { "type": "string", "enum": ["PURCHASE", "LEAD", ...] }
      },
      "version": "2.1.0"
    }
  }
}

Buyer's SDK caches by URI@digest. Subsequent get_products responses reference by digest; if buyer already has the extension cached, the inline definition is omitted.

Direct URI fetch fallback (GET https://meta.adcp/extensions/meta_pixel) is supported for tooling/debugging but the primary path is bundled-in-get_products.

Why this works:

  • No extra round trips
  • Cacheable across products that share extensions (Meta has 50 products referencing meta_pixel; buyer fetches once)
  • Digest-pinned (no silent drift)
  • Owned by the right party (Meta's agent is authoritative for Meta's extensions)
  • No central registry needed; each platform is its own canonical source

6. Brand context via brand.json with explicit override

When a manifest carries brand: { domain: "acme.com" }, the seller pulls brand context from brand.json. Override path for missing/stale brand.json:

interface CreativeManifest {
  brand?: { domain: string };
  brand_kit_override?: {
    logo?: AssetRef;
    colors?: { primary?: string; secondary?: string; accent?: string };
    voice?: string;
    tagline?: string;
  };
}

brand_kit_override takes precedence over brand.json for supplied fields.

7. validate_input primitive — including nondeterministic generative

validate_input({
  manifest: CreativeManifest,
  format_ids?: string[],
  product_ids?: string[]
})  { results: ValidationResult[] }

Buyer dry-run, format-discovery filter, catalog suitability check; build_creative calls internally.

Deterministic platforms: validate predictively, reject the input. predicted carries the platform's estimate.

Nondeterministic platforms (Veo, Sora, Runway-class): predictive validation is a category error. Two protocol obligations:

  1. Platform MUST run its own post-synthesis QA loop and surface only validated outputs.
  2. If exhausted without a valid artifact, return task_failed with structured synthesis_failed reason carrying constraints unmet, attempts, suggested adjustments.

8. preview_creative updates

Single tool, response shape adapts. #3268 single-render hoist applies for direct mode; nested previews[] for multi-render. New optional preview_subset_ids?: string[].

9. build_creative — generative as capability with typed inputs

build_creative({
  format_id: string,                    // canonical the output must satisfy
  inputs: BuildCreativeInputs,          // typed vocabulary
  brand?: { domain: string }
})  CreativeManifest

BuildCreativeInputs is a typed map (see §5 vocabulary). Different generative shapes use different combinations. Calls validate_input internally on produced manifest.

10. SDK codegen + canonical catalog distribution

Compiled into SDKs:

  • 11 canonical format definitions (with their tracking specs)
  • Canonical patterns (cta_vocabulary, destination_kinds, brand_safety_frameworks, universal_macros)
  • Asset_group_id vocabulary

Runtime-fetched (cached by URI@digest):

  • Platform extensions (bundled in get_products responses)
  • Third-party format definitions (URI-form format_id)
  • Creative-agent supported formats (via creative.supported_formats field on get_adcp_capabilities)

Generated bindings per language:

// TypeScript — @adcp/client
import { CanonicalFormats, CtaVocabulary, UniversalMacros } from '@adcp/client';

const reels = CanonicalFormats.videoHosted({
  orientation: 'vertical',
  duration_ms_range: [6000, 60000]
});

Type-safe parameterization, IDE autocomplete on canonical asset_group_id slot names, version-pinned canonical catalog (digest mismatch detectable), local validation without network.

Codegen scaffolding ships in 3.1 (preview); stable for TypeScript and Python in 3.2.

11. Discovery surfaces by agent role; list_creative_formats deprecated and back-compat-wrapped

  • Sales agents: get_products is the v2 discovery surface for product-bound formats. Bundles platform extensions inline.
  • Creative agents: declare which canonical formats they can build via creative.supported_formats on get_adcp_capabilities. Each entry uses the same ProductFormatDeclaration shape as products' inline format — keyed by canonical name with parameters and inputs. Creative agents may also expose sync_creatives for ad-server use cases.

Deprecation timeline (revised): v1 list_creative_formats deprecated in 3.x but functional through 4.0; sellers SHOULD provide server-side flatten wrappers that derive v1 shape from v2 product format declarations. Removed at 5.0.

12. Schema additions and drive-by fixes

Phase 1 (#3307) shipped:

  • zip asset type at static/schemas/source/core/assets/zip-asset.json (replaces the earlier delivery_type addition on html/javascript)
  • asset-group-vocabulary.json, scenes.json
  • video.mdx + audio.mdx asset_type fixes

Phase 2 (this RFC, ✅ in #3307):

  • schemas/v3/formats/canonical/{image,html5,display_tag,image_carousel,video_hosted,video_vast,audio_hosted,audio_daast,sponsored_placement,responsive_creative,agent_placement}.json plus _base.json with shared parameter fields including inputs and production_window_business_days
  • schemas/v3/core/product-format-declaration.json — keyed-by-canonical-name structure
  • schemas/v3/core/platform-extension-ref.json — URI + content-digest reference
  • schemas/v3/creative/scenes.json — typed scene-by-scene structure for build_creative input
  • schemas/v3/creative/validate-input-{request,response,result}.json — buyer dry-run primitive
  • creative.supported_formats field added to get_adcp_capabilities response (creative-agent discovery)
  • format.json slot declarations (Individual* and Group*) gain optional asset_group_id field for v1↔v2 migration bridge
  • product.json adds optional format field; creative-manifest.json adds optional brand and brand_kit_override

What dissolves

  • composition: 'slot_fill' | 'surface_composed' — derivable from canonical declaration.
  • expansion: 'per_offering' | 'per_item' — derivable from field_bindings shape.
  • output_modality — derivable from canonical's output asset_type.
  • promoted_offerings campaign mode — collapses into product format declarations + field_bindings.
  • Generative as a format familybuild_creative capability with typed inputs.
  • Brand identity slots — implicit context via brand.json + brand_kit_override.
  • list_creative_formats as a single tool — split by agent role.
  • Tracking as a separate canonical pattern — baked into each canonical format.
  • canonical discriminator field — replaced by format-keyed-by-name.
  • iab_size redundant labeling — derive from dimensions.
  • Polymorphic asset_types within image/video/audio — split by tracking model into separate canonicals.
  • build_capability and build_capability_ref as separate concepts (r4) — collapsed into inputs directly on the format declaration. External creative agents are invisible to the buyer; supply-chain relationships stay off the protocol surface.
  • creative_build_capabilities field name (r4) — renamed to creative.supported_formats; same ProductFormatDeclaration shape as products' inline format. One primitive, two homes.
  • Sales-agent build_creative exposure (r4) — build_creative is a creative-agent surface only. Sales agents expose get_products and sync_creatives; creative agents may also expose sync_creatives for ad-server use cases.
  • delivery_type as a discriminator on html-asset.json/javascript-asset.json (Phase 1 scrub) — URL-delivered HTML/JS routes through url-asset.json with appropriate url_type; HTML5 zip bundles get a proper first-class zip asset type.

What does not dissolve:

  • Named platform-specific formats as a v1-era pattern — they keep working through 3.x/4.x. v2 is the new path; sellers migrate when their organizational reality permits.

Migration

Realistic adoption by adopter type:

Adopter Cost Realistic timeline Notes
DSP buyer agents (TTD-shaped) Low 3.1-3.2 Reading inline product format declarations is strictly easier than v1 cross-reference.
SSP/sales agents (Magnite, PubMatic, retail media) Medium-high 3.3-4.0 Format ownership split across teams in retail media.
Walled gardens (Meta, Google, Amazon, TikTok, Snap, Pinterest) High, low motivation 4.0-5.0 if at all Multi-team metadata ownership, legal review on format changes. Realistic only when AAO ships a translator from existing format docs to v2 declarations.
Creative agents (AudioStack-shaped) Low, high motivation 3.1-3.2 creative.supported_formats matches what they want to declare.
Publisher direct (GAM/prebid path) Medium Blocked on native pre-audit Native shapes are a major adoption driver.
Phase What ships
3.1 (preview) ✅ Phase 1 + Phase 2 in #3307: vocabulary registry, scenes, zip asset type, 11 canonical format definitions, product-format-declaration.json, inputs on format declarations, creative.supported_formats on get_adcp_capabilities, validate_input primitive, preview_creative updates, asset_group_id on format slots (v1↔v2 bridge), SDK codegen scaffolding, URI/canonical/digest-pinned format_id, brand_kit_override. v1 fully supported. list_creative_formats deprecated.
3.2 Vocabulary expansion. SDK codegen stable (TS + Python). Native canonical proposal after TemplateCreative + OpenRTB Native 1.2 audit. Reference adopters (sample sellers + AudioStack) migrate.
3.3 Third-party format authoring guide finalized. Soft warnings on non-canonical slots. v1 product format references soft-deprecated. Server-side flatten wrappers documented.
4.0 v2 product format declarations are the recommended path. v1 still supported with flatten wrappers. Canonical vocabulary becomes MUST for spec-defined formats.
5.0 list_creative_formats removed. v1 named-format references removed. v2-only path.

Phase gating

  • sponsored_placement cluster work blocked on TemplateCreative + OpenRTB Native 1.2 pre-audit (separate issue to be filed).
  • Walled-garden migration gated on AAO providing a translator from existing format docs to v2 declarations.

Vocabulary governance

Asset_group_id and canonical pattern extensions go through PR + rationale + ≥1 reference adopter, AAO maintainer review, versioned files with content digests.

Soft-warn semantics: validators MAY emit soft warnings on non-canonical asset_group_id values. Warnings are CLI/SDK lint output, NOT protocol-level diagnostics returned in warnings[] fields. Sellers MUST NOT thread soft warnings into protocol responses.

Forward notes (not in v2 scope)

Prior art and references

  • OpenRTB Native 1.2 — model for asset_group_vocabulary
  • VAST 4.x — universal ad ID + verification node (tracking mixin separability); SIMID extension
  • OpenRTB 2.5 BidExt.vast.vpaid — VPAID via vpaid_enabled: true flag
  • DBCFM proposal lifecycle — prior art for mixin precedence
  • AdCP request-signing vector positive/002: Content-Digest header value doesn't match body hash #2335 — digest-pinning failure mode
  • AdCP Simplify PreviewCreativeResponse for the single-render case #3268PreviewCreativeResponse single-render hoist; pairs with scenes.json
  • TMP schema-pinning convention — content-digest-pinned schema references
  • MRAID 3.0 — model for HTML5 canonical's interactive measurement
  • IAB Open Measurement SDK — model for video_hosted and html5 canonical measurement integration
  • DAAST 1.1 — audio analog to VAST; basis for audio_daast canonical

Files affected

✅ Phase 1 (#3307) shipped:

  • static/schemas/source/core/asset-group-vocabulary.json
  • static/schemas/source/creative/scenes.json
  • html-asset.json, javascript-asset.json (delivery_type)
  • docs/creative/channels/video.mdx, audio.mdx fixes

Phase 2-3 (this RFC):

  • schemas/v3/creative/canonical-formats.json (index)
  • schemas/v3/formats/canonical/{image,html5,display_tag,image_carousel,video_hosted,video_vast,audio_hosted,audio_daast,sponsored_placement,responsive_creative,agent_placement}.json
  • schemas/v3/creative/product-format-declaration.json, validate-input-{request,response,result}.json
  • schemas/v3/patterns/{cta-vocabulary,destination-kinds,brand-safety-frameworks,universal-macros}.json
  • Modify backwards-compatibly: product.json (add format); creative-manifest.json (add brand and brand_kit_override); format.json slot declarations gain optional asset_group_id (v1↔v2 bridge); get_adcp_capabilities response gains creative.supported_formats

Tools:

  • SDK codegen scaffolding (TypeScript first, Python next)
  • Canonical catalog publishing pipeline with content digests
  • Server-side flatten wrapper reference implementation

Source

Surfaced by AdCP @adcp/client v6.0 SDK be-Emma test (AudioStack adapter) producing #3268, #3269, #3270 in one round. Audits of 12 social/retail platform adapters (86 formats) and prebid salesagent (GAM tag handling). Three independent specialist reviews (synthesis) plus collaborative working-example walkthrough informed the architecture.

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.creativerfcProtocol change — auto-adds to roadmap boardschemaJSON Schema source-of-truth: definitions, codegen artifacts, validation, hygiene

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions