Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7b8c004
feat(creative): v2 Phase 1 — asset_group vocabulary, scenes, delivery…
bokelley Apr 26, 2026
8d090c0
fix(creative): address Phase 1 review catches — audio.mdx VAST bug + …
bokelley Apr 26, 2026
8fdc407
fix(docs): vast_version must be a single string, not an array
bokelley Apr 26, 2026
6324fba
chore(changeset): correct phase 1 bump from patch to minor
bokelley Apr 27, 2026
4df03c2
refactor(creative): replace delivery_type addition with zip asset type
bokelley Apr 27, 2026
77398b3
feat(creative): v2 Phase 2 — canonical format catalog, ProductFormatD…
bokelley Apr 27, 2026
4b0231a
docs(creative): add v2 overview with worked examples (Meta Reels, IAB…
bokelley Apr 27, 2026
170fa41
refactor(creative): collapse build_capability into format inputs; add…
bokelley Apr 27, 2026
8db52b9
docs(creative): drop "generative formats" framing in v2 overview; add…
bokelley Apr 28, 2026
5b74e25
refactor(creative): rename canonical formats — asset_pool_composed → …
bokelley Apr 28, 2026
202ce2e
feat(creative): v2 migration guide + 5 reference fixtures + product.j…
bokelley Apr 28, 2026
77d69b0
fix(creative): address review feedback — doc/schema mismatches, missi…
bokelley Apr 28, 2026
6dcf8dd
feat(creative): manifest inputs field; bundled extensions schema + fi…
bokelley Apr 28, 2026
53f8dbe
refactor(creative): drop inputs concept, add format_kind discriminato…
bokelley Apr 28, 2026
a7734e3
docs(creative): tighten style_reference description with brand.json r…
bokelley Apr 28, 2026
550b042
fix(creative): doc/schema cleanup; full canonical fixture coverage; n…
bokelley Apr 29, 2026
c5de39c
docs(creative): server-side implementation considerations for v2 sale…
bokelley Apr 29, 2026
32b4a7f
Merge remote-tracking branch 'origin/main' into bokelley/v2-phase1-vo…
bokelley Apr 30, 2026
19e6a30
feat(creative): v2 review-feedback round — format_options array, cano…
bokelley Apr 30, 2026
23c8bea
fix(creative): drop invented creative_agents field from v2 third-part…
bokelley Apr 30, 2026
6882b0c
feat(creative): add production-source axis (image_source / video_sour…
bokelley Apr 30, 2026
e074941
feat(creative): v2 red-team schema fixes — manifest v2 path, carousel…
bokelley Apr 30, 2026
55edddf
docs(creative): v2 red-team docs round — glossary, vocabulary table, …
bokelley Apr 30, 2026
601bd95
docs(creative): expand v2 creative-agent migration walkthrough
bokelley Apr 30, 2026
76f7990
Merge remote-tracking branch 'origin/main' into bokelley/v2-phase1-vo…
bokelley Apr 30, 2026
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
30 changes: 30 additions & 0 deletions .changeset/v2-phase1-vocab-scenes-zip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
"adcontextprotocol": minor
---

feat(creative): v2 Phase 1 — asset_group_id vocabulary registry, `scenes` schema, `zip` asset type, video/audio mdx asset_type fixes

First PR implementing the v2 creative formats RFC (#3305). Backwards-compatible additions only — no v1 producers are affected. Minor bump because this introduces new schemas (`asset-group-vocabulary.json`, `scenes.json`, `zip-asset.json`), which are additive features rather than bug fixes.

**New schemas:**

- `static/schemas/source/core/asset-group-vocabulary.json` — canonical registry of `asset_group_id` values (the seven existing catalog vocab entries plus 12 audit-driven additions: `video_vertical`, `video_horizontal`, `audio`, `companion_image`, `companion_banner`, `brand_name`, `body_text`, `cards`, `landing_page_url`, `privacy_policy_url`, `youtube_video_id`, `pin_id`). Includes the `landing_page_url` aliases canonicalizing six different field names today (`click_url`, `link`, `final_url`, `link_url`, `click_through_url`, `landing_url`). Non-canonical IDs remain valid for platform-specific extensions; validators MAY soft-warn on non-canonical usage.

- `static/schemas/source/creative/scenes.json` — typed scene-by-scene structure used as input to `build_creative` for generative video platforms. Each scene has `order`, `duration_ms`, `description`, optional `vo` and `caption`. Renamed from "storyboard" to avoid collision with the testing-harness storyboard concept; description disambiguates from `reference-asset.json` `purpose: "storyboard"` (which describes a reference asset, not a structured plan).

- `static/schemas/source/core/assets/zip-asset.json` — new asset type for bundled creatives delivered as zip archives (HTML5 banners with index.html + CSS + JS + images, MRAID-compatible interactive ads). Carries `url`, optional `max_file_size_kb`, `entry_point`, `allowed_inner_extensions`, `backup_image_url`, and SHA-256 `digest` for integrity. Distinct from inline HTML (`html` asset) and from third-party tag URLs (`url` asset with appropriate `url_type`).

**Registry updates:**

- `static/schemas/source/creative/asset-types/index.json` — added `zip` entry pointing at the new schema
- `static/schemas/source/core/format.json` — added `IndividualZipAsset` and `GroupZipAsset` branches to the format declaration oneOf
- `static/schemas/source/core/offering-asset-group.json`, `creative-manifest.json`, `creative-asset.json`, `creative/list-creatives-response.json` — added `zip-asset.json` to manifest/asset-group oneOf branches so manifests can carry zip assets

**Doc fixes:**

- `docs/creative/channels/video.mdx` — corrected three format-definition examples that used `asset_type: "url"` + `asset_role: "vast_url"` / `"vpaid_url"`, contradicting the schema-correct `asset_type: "vast"` used elsewhere in the same file. Updated VPAID examples to use `asset_type: "vast"` with `vpaid_enabled: true` in requirements.
- `docs/creative/channels/audio.mdx:200` — same bug pattern: `asset_type: "url"` for what should be a VAST audio tag. Corrected to `asset_type: "vast"` with `delivery_type: "url"`; renamed slot key from `vast_url` to `vast_tag` for clarity.

**Why minor (not patch):** new schemas and a new asset type are additive features — patch is reserved for bug fixes only. **Why not major:** no breaking changes; v1 producers and consumers continue to work unchanged. The new `zip` asset type is purely additive — receivers that don't recognize it ignore it via standard discriminator-mismatch handling.

Tracks #3305 (v2 RFC). Phase 1 lays foundational primitives; subsequent phases build the canonical format catalog, `ProductFormatDeclaration` schema, and tools on top of these primitives.
84 changes: 84 additions & 0 deletions .changeset/v2-review-feedback-format-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
"adcontextprotocol": minor
---

feat(creative): v2 review-feedback round — `format_options` array, canonical `status`, hosting paragraph, third-party creative-agent worked example

Addresses external review feedback on RFC #3305 / PR #3307 before the 3.1.0 beta cycle opens.

**Schema changes:**

- **`product.format` → `product.format_options: [ProductFormatDeclaration]` (array).** Restores v1 `format_ids` cardinality on the v2 path. The 90% case is a single-element array (one canonical narrowed for the product); multi-element arrays declare that the product accepts any of the listed format options (e.g., a placement that takes EITHER Flashtalking-served `html5` OR an internal `display_tag`; a video product that accepts a hosted upload OR a VAST tag). Buyers pick which option they're shipping at `sync_creatives` time by aligning their manifest to the matching declaration's `format_kind`. Mutually exclusive with `format_ids` via the existing `oneOf`.
- **`status: "stable" | "preview" | "deprecated"` field on canonical format `_base.json`.** Default `stable`. Lets the spec ship not-yet-fully-settled canonicals (`agent_placement` and `responsive_creative` in 3.1) with explicit notice that their parameter shape and tracking model MAY break in 3.2 once 2-3 adopters have built against them. The other 9 canonicals are anchored in stable IAB / platform standards and stay `stable`.

**Doc changes:**

- **Worked example: third-party creative agent path (Flashtalking + NYTimes display).** Adds a multi-actor walkthrough alongside the existing single-actor host-read example: buyer reads NYTimes capabilities → sees declared `creative_agents` and the resolved `supported_formats` projection → calls Flashtalking's `build_creative` → ships the manifest to NYTimes via `sync_creatives`. The seller validates against the canonical, NOT against Flashtalking's narrowing — that's the creative agent's contract with the buyer. Closes a gap where the r4 collapse of `build_capability` into format slots wasn't documented for the third-party-creative-agent flow.
- **Platform extension hosting expectations.** Adds a paragraph to the "Platform extensions — distribution" section documenting hosting role (publisher's subdomain hosts the canonical artifact), caching expectations (`Cache-Control: public, max-age=31536000, immutable` enabled by digest pinning), availability targets (≥99.9% / 30 days), and graceful-degradation semantics on 404 (treat extension as unavailable; don't fail the buy). AAO mirror is best-effort fallback, not normative.
- **Adoption-driven `format_ids` removal trigger.** v1 `format_ids` is removed in 5.0 — but the trigger is adoption-driven, not date-driven. AAO computes the ratio of registered sales agents declaring `format_options` from cached `get_products` capabilities responses. When `format_options` adoption crosses 80% and stays there for 30 consecutive days, the 5.0 cut sequence opens. Until then, both shapes remain valid.

**Schema housekeeping:**

- Added a description note on `validate-input-response.json` documenting the intent behind the 3-schema split (`request` / `response` / `result`): the `Result` type is split for planned reuse by adjacent async-validation surfaces (per-batch result envelopes on `build_creative` async paths, asynchronous canonical-against-product validation in `sync_creatives`). Producers that only need the synchronous batch shape today MAY treat the split as YAGNI; the schema reuse anchors the violation/retry shape so downstream surfaces don't drift.
- Updated all 12 v2 reference fixtures (`static/examples/products/v2/*.json`) plus the `meta_with_bundled_extensions.json` get_products response fixture to use the new `format_options` array shape. All 13 fixtures still validate via `npm run test:v2-fixtures`.
- Updated `tests/schema-validation.test.cjs` core-required-fields rule to assert `format_options` (not `format`) on the v2 oneOf branch.

**Why minor:** structural rename of `product.format` → `product.format_options` is technically breaking for anyone who built against the v2 path during the preview window, but the v2 path was only landed in this PR (#3307) and is not yet released — no published 3.x version carries `format`. The shipping shape is `format_options`. Anyone building against the preview branch should re-pull. The other changes are additive.

**Red-team round (must-fix + should-fix + nits)** — substantive cleanup against three parallel red teams (protocol-expert, adtech-product-expert, docs-expert):

Schema fixes:
- Manifest v2 path. `creative-manifest.json` and `creative-asset.json` now carry `oneOf(format_id v1 path | format_kind v2 path)` with explicit `not` on each branch. New `/schemas/core/canonical-format-kind.json` enum backs the v2 path. Optional `capability_id` field disambiguates when a product's `format_options` carries multiple declarations sharing the same `format_kind`. Without this, v2 products had no v2 manifest counterpart.
- `ProductFormatDeclaration` grows `capability_id` (stable identifier for routing) and `applies_to_channels` (subset of the product's channels this declaration applies to — lets a multi-channel product carry channel-specific format_options).
- `audio_source` enum widened to match `image_source` / `video_source` (now 5-value: `buyer_uploaded | publisher_host_recorded | seller_pre_rendered_from_brief | seller_human_designed | agent_synthesized`). TTS-from-brief and studio-produced audio now expressible.
- `product.json` oneOf branches got explicit `not: required: [other]` to truly exclude both `format_ids` AND `format_options` being present.
- Stale "inputs" references in `get-adcp-capabilities-response.json supported_formats` descriptions replaced (the concept was dropped in r4 — collapsed into slots).
- `image_carousel` got a default slots declaration (`cards` slot, asset_type: object) plus a normative `card_shape` parameter documenting the per-card object structure (media + headline + landing_page_url). `assets.cards` is now the unambiguous array-under-one-key contract; per-card key conventions (card_0_headline, cards.0.headline) are forbidden.
- Slots inline default added to all 11 canonicals (previously only on 3). SDK codegen now produces typed slot lists for every canonical.
- `synthesis_nondeterministic` × `*_source` compatibility documented in `_base.json` (incompatible with `buyer_uploaded` and `publisher_host_recorded`).
- `platform-extension-ref` digest collision behavior documented (within a single response, divergent digests for the same uri MUST fail closed; across responses, divergence is normal).
- `status: preview` deprecation pathway: `since_version` + `migration_target_version` siblings on canonical `_base.json`, plus a stabilization rubric ("preview → stable when 2 adopters ship + 90 days no breaking change").
- Veo fixture used `audio_source` / `buyer_audio_acceptance` on a `video_hosted` format. Renamed to `video_source` / `buyer_video_acceptance`.

Doc additions:
- v2-overview.mdx glossary covering ~25 v2 terms.
- Asset group vocabulary table (was previously only in the JSON schema).
- "Two axes" section refined to show the unified 5-value source enum.
- Tracker assembly under seller-rendered sources documented (macro-substituted vs sync-creatives tracker block).
- "Channels not yet canonicalized" section (native, linear/addressable TV, OOH/DOOH, audio DAI, in-game, live streaming).
- Worked examples added for: generative DSP (universalads-class, `image_source: seller_pre_rendered_from_brief`), multi-format product (Flashtalking html5 OR internal display_tag), `sponsored_placement` with `item_production_model` (1 brief × N items → N creatives).
- Hosting reframed as two paths: open-ecosystem (publisher-hosted) vs closed-platform (AAO-mirror-translated, normative for walled gardens).
- `validate_input` "when to use" decision rule + comparison table with `build_creative` and `sync_creatives`.
- Discovery + validation scaling guidance (client-side filter + multi-target validate_input).
- Generative-DSP narrative weight tuned (demoted to forward-looking subsection — universalads/Pencil/AdCreative.ai are real but small share of 2026 spend).
- Creative-agent business-model paragraph clarifying that v2 disaggregation is conceptual; creative agents continue to host their produced creatives' bytes and instrument tracking via platform extensions.
- Preview canonicals stabilization rubric (`responsive_creative` and `agent_placement` re-evaluated for stable status by 3.3 if adopters land in 3.1-3.2).
- Phase 4 SDK codegen blocker callout in the status banner.
- Phase 3 fixture count reconciled (12 product fixtures + 1 response fixture).

Migration doc additions:
- v1 deprecation calendar floor + ceiling (2027-Q4 floor, 2029-Q1 ceiling) bounding the adoption-driven trigger.
- Adoption-trigger metric definition (denominator + numerator + AAO publishing surface).
- `creative_id` stability invariant across v1 ↔ v2.
- "What v2 gives you that OpenRTB doesn't" subsection (canonical-as-contract decoupling, runtime discovery, declared production source, canonical tracking model).

Cross-doc references:
- v2 preview banners on `formats.mdx`, `key-concepts.mdx`, `generative-creative.mdx`, `specification.mdx`, `implementing-creative-agents.mdx`, `asset-types.mdx` so readers landing from search have a signpost.

`asset-types.mdx` updated for v2 with `asset_group_id` framing, full v2 asset_type table including `brief` / `catalog` / `zip` / `markdown` / `webhook` / `object`.

**Production-source taxonomy (universalads / generative-DSP gap):**

The audio_hosted canonical previously handled "who renders" via `audio_source` but with a narrower 3-value enum than image/video. The asymmetry forced generative-DSP-shaped adopters to either fudge `composition_model` or invent platform extensions to express what's actually a common pattern.

This change adds:

- `image_source` on `image` — `buyer_uploaded | seller_pre_rendered_from_brief | seller_human_designed | agent_synthesized` (default `buyer_uploaded`). Plus `buyer_image_acceptance: accepted | rejected`.
- `video_source` on `video_hosted` — same enum and pattern as `image_source`. Plus `buyer_video_acceptance: accepted | rejected`.
- `item_production_model` on `sponsored_placement` — same enum, applied per catalog item. Captures the multi-output generative pattern (1 brief × N catalog items → N rendered creatives) under the existing `sponsored_placement` canonical without requiring a 12th canonical.

These are informational fields, not the binding contract — the format's `slots` declaration is the contract. The `*_source` fields let buyers pick products whose production model fits their workflow (in-house pre-rendered vs upstream creative agent vs seller-driven generative).

The v2-overview.mdx narrative now explicitly differentiates the two orthogonal axes — `composition_model` (how the surface composes per-impression: deterministic vs algorithmic) and per-canonical production source (who renders, and when). Conflating them was the gap that left generative DSPs without a clean expression in v2.

Tracks #3305 (v2 RFC) and #3307 (preview branch).
28 changes: 28 additions & 0 deletions docs/creative/asset-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,39 @@ description: "AdCP asset types define standardized properties for images, video,
"og:title": "AdCP — Asset Types"
---

> **v2 readers**: this page describes asset types and their payload shapes — the same in v1 and v2. For how assets map to v2 format slots via `asset_group_id` (canonical vocabulary), see [v2-overview](/docs/creative/v2-overview) and [v2-migration](/docs/creative/v2-migration). v1 uses `asset_id` + `asset_role`; v2 uses `asset_group_id` referencing the canonical vocabulary registry. Both paths use the same asset payload schemas — only the slot-key vocabulary differs.

Creative formats in AdCP use standardized asset types with well-defined properties. Assets are the discrete, typed building blocks used by formats to define requirements and by manifests to supply concrete values.

Standardizing asset types ensures consistency across formats and makes requirements easier for buyers and systems to understand.

## Asset types in v2

The full set of asset types valid in a v2 format `slots` declaration's `asset_type` field:

| asset_type | What it carries | Where defined |
|---|---|---|
| `image` | Image file (jpg/png/gif/webp/svg) | `/schemas/core/assets/image-asset.json` |
| `video` | Video file (mp4/webm/mov) | `/schemas/core/assets/video-asset.json` |
| `audio` | Audio file (mp3/aac/wav) | `/schemas/core/assets/audio-asset.json` |
| `text` | Plain text (headline, body, script) | `/schemas/core/assets/text-asset.json` |
| `markdown` | Markdown text | `/schemas/core/assets/markdown-asset.json` |
| `url` | URL with `url_type` discriminator (clickthrough, tracker_pixel, third-party tag) | `/schemas/core/assets/url-asset.json` |
| `html` | Inline HTML | `/schemas/core/assets/html-asset.json` |
| `css` | CSS rules | `/schemas/core/assets/css-asset.json` |
| `javascript` | JavaScript | `/schemas/core/assets/javascript-asset.json` |
| `vast` | VAST tag (URL or inline XML), VAST 2.x-4.x | `/schemas/core/assets/vast-asset.json` |
| `daast` | DAAST tag (URL or inline XML), 1.0-1.1 | `/schemas/core/assets/daast-asset.json` |
| `webhook` | Webhook URL for async creative production | `/schemas/core/assets/webhook-asset.json` |
| `brief` | Free-text creative brief (input to generative production) | `/schemas/core/assets/brief-asset.json` |
| `catalog` | Reference to a synced catalog (sponsored_placement) | `/schemas/core/assets/catalog-asset.json` |
| `zip` | Zip archive (HTML5 banner bundle) | `/schemas/core/assets/zip-asset.json` |
| `vast_tracker` | Single VAST `Tracking` event URL (decomposed) | `/schemas/core/assets/vast-tracker-asset.json` |
| `daast_tracker` | Single DAAST `Tracking` event URL (decomposed) | `/schemas/core/assets/daast-tracker-asset.json` |
| `object` | Structured object (image_carousel `cards`, scenes for generative video) — sub-shape declared by the canonical | (no standalone schema; per-canonical sub-shape) |

In a v2 manifest's `assets` map, the **slot key** is the canonical's `asset_group_id` (e.g., `image_main`, `video_main`, `script`, `cards`, `landing_page_url`) and the **value** carries the matching asset payload with its `asset_type` discriminator. The format declaration's `slots[].asset_type` tells the validator which payload schema applies.

## Important: Payload vs Requirements

For payload schemas (the structure of the actual asset data supplied in creative manifests), see:
Expand Down
6 changes: 3 additions & 3 deletions docs/creative/channels/audio.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ Multi-segment audio assembled dynamically:
"id": "audio_30s_vast"
},
"assets": {
"vast_url": {
"asset_type": "url",
"url_type": "tracker",
"vast_tag": {
"asset_type": "vast",
"delivery_type": "url",
"url": "https://ad-server.brand.com/audio-vast?campaign={MEDIA_BUY_ID}&cb={CACHEBUSTER}"
}
}
Expand Down
17 changes: 8 additions & 9 deletions docs/creative/channels/video.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,11 @@ For third-party ad servers:
"assets": [
{
"asset_id": "vast_tag",
"asset_type": "url",
"asset_role": "vast_url",
"asset_type": "vast",
"item_type": "individual",
"required": true,
"requirements": {
"vast_version": ["3.0", "4.0", "4.1", "4.2"]
"vast_version": "4.2"
}
}
]
Expand All @@ -445,13 +444,13 @@ For third-party ad servers:
"assets": [
{
"asset_id": "vpaid_tag",
"asset_type": "url",
"asset_role": "vpaid_url",
"asset_type": "vast",
"item_type": "individual",
"required": true,
"requirements": {
"vpaid_version": ["2.0"],
"api_framework": "VPAID"
"api_framework": "VPAID",
"vpaid_enabled": true
}
}
]
Expand Down Expand Up @@ -762,13 +761,13 @@ VPAID (Video Player Ad-Serving Interface Definition) enables interactive video a
"assets": [
{
"asset_id": "vpaid_tag",
"asset_type": "url",
"asset_role": "vpaid_url",
"asset_type": "vast",
"item_type": "individual",
"required": true,
"requirements": {
"vpaid_version": ["2.0"],
"api_framework": "VPAID"
"api_framework": "VPAID",
"vpaid_enabled": true
}
}
]
Expand Down
Loading
Loading