diff --git a/.changeset/canonical-formats-adopter-feedback.md b/.changeset/canonical-formats-adopter-feedback.md new file mode 100644 index 0000000000..8b262f79cc --- /dev/null +++ b/.changeset/canonical-formats-adopter-feedback.md @@ -0,0 +1,17 @@ +--- +"adcontextprotocol": minor +--- + +canonical-formats: five adopter-flagged additions before lock. Each surfaces guidance that was implicit in the spec but not findable; one resolves a normative silence. + +**1. "What `format_kind` is NOT for" decision rule** (canonical-formats.mdx). Adopters seeing the broadcast / DOOH / generative annotations were tempted to propose `format_kind: dooh_image` or `format_kind: broadcast_video` in 3.2. New section enumerates the six axes (creative type / production model / slot shape / channel / measurement / targeting) and the rule of thumb: new `format_kind` ONLY when the creative ASSET is structurally different. All 50 ad formats in the catalog ship via this rule; zero new canonicals added for broadcast / DOOH / native / generative. + +**2. `slots_override` authoring decision rule** (canonical-formats.mdx). When to use it vs leave it off was implicit across the four annotation patterns. New section + table makes it explicit: would a buyer composing the manifest list **different assets** vs the canonical's defaults? Yes → `slots_override`. No → omit. Worked cases: IAB MREC (default), native standard (override), DOOH (default), broadcast (default), generative (override), host-read podcast (override). + +**3. End-to-end `adagents.json` fetch flow worked example** (canonical-formats.mdx). The pieces were documented separately (publisher catalog, property scoping, capability_id resolution, community-mirror fallback, supersession). New section walks the full buyer journey in order: `Product` with `publisher_properties` → fetch `/.well-known/adagents.json` → fall back to AAO mirror on 404 → check `superseded_by` → scope `formats[]` by `applies_to_property_ids` → resolve `capability_id` against same-file `formats[]`. Concrete payload sequence included. + +**4. Multi-size fan-out normative decision** (canonical-formats.mdx + error code prose). The spec was silent on whether SDKs MAY fan out a multi-size v2 declaration to N v1 format_ids via catalog lookup. Resolved as MAY-do non-normative: SDKs without catalog access emit only seller-asserted refs (the conservative wire shape, default normative behavior); SDKs with catalog access MAY synthesize the missing per-size refs. Either way, `FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE` MUST fire as a transparency advisory; `error.details.synthesized_refs` lists catalog-resolved entries when fan-out is in play. Two SDKs processing the same input may produce different `format_ids[]` lengths, but the advisory keeps consumers in sync. + +**5. `format_schema` fetch-contract test fixtures** (static/examples/format-schemas/). 14 paired positive + negative test vectors covering all 7 failure-mode categories per the normative contract: digest verification, transport (https-only / redirect / oversize), SSRF (RFC 1918 / metadata endpoint), `$ref` sandboxing (cross-origin / depth-exceeded), schema-compile budget (catastrophic regex), schema validity (body is JSON but not a valid schema), graceful degradation (404 with/without cache). Each fixture documents the `setup` to simulate, the `expected_outcome`, the `expected_error_code`, and the rationale linking back to the contract clause. README at `static/examples/format-schemas/README.md` documents shape and usage. Cross-SDK conformance harness tracked as follow-up #4699. + +Validation: schema build clean, 14 canonical fixtures + 28 negative fixtures (was 19) + 50-entry catalog convention lint all green. diff --git a/.changeset/canonical-formats.md b/.changeset/canonical-formats.md new file mode 100644 index 0000000000..464d53726f --- /dev/null +++ b/.changeset/canonical-formats.md @@ -0,0 +1,47 @@ +--- +"adcontextprotocol": minor +--- + +**Canonical formats (AdCP 3.1).** v2 introduces a structured creative-format vocabulary that buyers and sellers can validate against without per-seller integration code. 13 canonical `format_kind` values (image, html5, display_tag, image_carousel, video_hosted, video_vast, audio_hosted, audio_daast, sponsored_placement, native_in_feed, responsive_creative, agent_placement, custom) with a two-axis model: `format_kind` names the creative TYPE; `asset_source` names the production model (buyer_uploaded / publisher_host_recorded / seller_pre_rendered_from_brief / seller_human_designed / agent_synthesized). Products carry `format_options[]` declarations narrowing a canonical with `params`, `slots`, `applies_to_channels`, optional `capability_id` for multi-format routing, and optional `experimental` flag. The full reference is at `docs/creative/canonical-formats.mdx`. + +**Wire-shape details adopters care about:** + +- `v1_format_ref` is ALWAYS an array of `{agent_url, id}` entries — single-ref is `[{...}]`. Multi-size declarations carry one ref per size in `params.sizes[]`. The `FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE` error code surfaces when ref count < sizes[] count. +- v1 catalog's `canonical:` annotation is ALWAYS an object — minimal `{ "kind": "image" }`, rich `{ "kind", "asset_source", "slots_override" }`. The object form is what lets the 8 generative catalog entries (`display_*_generative`) project losslessly to v2: buyer ships a text prompt, not image bytes. +- Display canonicals (`image`, `html5`, `display_tag`) support three size modes (mutex-enforced at schema layer): fixed `width+height`, multi-size `sizes: [{w,h}]` (mirrors OpenRTB `banner.format[]`), responsive `min_width/max_width/min_height/max_height`. The same product can carry N format_options across the three modes. +- `ProductFormatDeclaration.canonical_formats_only: true` is the v2-only marker (mutex with `v1_format_ref`). +- `format_kind: "custom"` requires `format_shape` (vocabulary entry) + `format_schema` (URI+digest) and either `canonical_formats_only: true` OR `v1_format_ref`. + +**Publisher catalog (`adagents.json formats[]`).** Publishers declare their format support once via top-level `formats[]` (with optional `applies_to_property_ids` / `applies_to_property_tags` scoping). Placements reference declarations by `capability_id`. For platforms that haven't adopted AdCP (Meta, TikTok, etc.), AAO publishes community-maintained adagents.json at `creative.adcontextprotocol.org/translated//adagents.json`; `superseded_by` field signals platform-adoption cutover. New media-buy filters `list_creative_formats(publisher_domain, property_id)` answer "what formats does this publisher accept?" with a normative resolution chain (publisher hosted → AAO mirror → agent-derived from products) and a response `source` field labeling which tier produced the list. + +**Where each piece of metadata lives (the "no new canonical" pattern).** Before reaching for a new canonical, the spec checks: production model → `asset_source`; slot shape → `slots_override`; channel → `applies_to_channels`; tracking / measurement → `sync_event_sources` / `event_log`. New canonical only when the CREATIVE ASSET is structurally different. Applied: generative, broadcast TV, DOOH, native all stay on existing canonicals via sibling refinement. Conversion pixels (Meta Pixel, GA4) explicitly belong on event_log, NOT on `platform_extensions` of a creative format. + +**Coverage at GA.** 50/50 ad formats in the AAO catalog annotated with the projection-ref object form. 7 UI scaffolding entries (`product_card_*`, `format_card_*`, `proposal_card_*`, `native_product_card`) split into `ui-element-formats.json` — they're agent-interface widgets, not ad formats; `list_creative_formats` returns them so consumers can resolve by `format_id`, but they never project to ad canonicals. + +**Error codes added** (all surfaced via response `errors[]` augmentation; non-fatal advisories): `FORMAT_PROJECTION_FAILED`, `FORMAT_DECLARATION_DIVERGENT`, `FORMAT_DECLARATION_V1_AMBIGUOUS`, `FORMAT_CAPABILITY_UNRESOLVED`, `FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE`, `PIXEL_TRACKER_LOSSY_DOWNGRADE`, `PIXEL_TRACKER_UPGRADE_INFERRED`. + +**Cross-version pixel_tracker contract (normative for SDK auto-negotiation)**: when a 3.1 buyer SDK talks to a 3.0.x seller that doesn't know `pixel_tracker`, the SDK MUST downgrade to v1 `{asset_type: url, url_type: tracker_pixel}` shape and emit `PIXEL_TRACKER_LOSSY_DOWNGRADE` with per-field details. Conversely a 3.1 SDK reading v1 trackers MUST upgrade by inferring event/method from `asset_id` conventions and emit `PIXEL_TRACKER_UPGRADE_INFERRED`. Both directions are lossy-with-advisory — no REFUSE branch, even for `method: js` (v1 sellers fire the URL as a GET; counter-based measurement increments, but JS-execution-dependent measurement like OMID-style verification won't run). Buyer-side decision per asset: accept the loss or route to a 3.1-capable seller. Full bidirectional mapping table documented in `pixel-tracker-asset.json` description. + +**Renderer-fired pixel tracker asset type (#4706)**: new `pixel_tracker` asset type at `static/schemas/source/core/assets/pixel-tracker-asset.json` — the generic web-pixel tracker primitive, applies to any web-rendered canonical (image, html5, image_carousel, responsive_creative, sponsored_placement, native_*, plus non-VAST/DAAST events on video_hosted/audio_hosted). Discriminated union with `event` (impression, viewable_mrc_50, viewable_mrc_100, viewable_video_50, click, custom), `method` (img, js), `url` (uri-template with universal-macros support), and `custom_event_name` (required when event is `custom`). Discriminator shape and event/method enums formalized in IAB OpenRTB Native 1.2 (`imptrackers[]` / `jstracker` / `eventtrackers[]` / `link.clicktrackers[]`). Scope is RENDERER-FIRED trackers — conversion pixels (Meta Pixel, GA4, server-side postbacks) stay on `sync_event_sources` / `event_log` per the format-vs-event_log boundary documented in canonical-formats.mdx. Formats with format-specific tracker structures keep their own primitives: `vast_tracker` (VAST ``), `daast_tracker` (DAAST parity). + +**Catalog tracker upgrade**: 45 ad-format payload entries in `server/src/creative-agent/reference-formats.json` previously carrying `impression_tracker` as `asset_type: "url"` upgraded to `asset_type: "pixel_tracker"` with `event: "impression"` + `method: "img"` — display / native / dooh / video_hosted / audio_hosted families all benefit. Plus 4 `slots_override` declarations on `native_standard` / `native_content` reshaped to use `pixel_tracker` for impression and click trackers. To match real measurement plans (impression + viewability + click trackers are commonly attached together), 90 additional optional slots added across the 45 web-rendered entries: each now carries `viewability_tracker` (`event: viewable_mrc_50`) and `click_tracker` (`event: click`) as optional `pixel_tracker` slots. Buyer populates only the trackers their measurement plan declares; absent slots are skipped. New vocabulary entries: `impression_tracker`, `click_tracker`, `viewability_tracker` in asset-group-vocabulary, all mapping to `asset_type: pixel_tracker`. + +**`pixel_tracker.event` enum mirrors IAB Native event-type registry**: 5 standardized event values plus `click` and `custom`. Maps 1:1 to IAB OpenRTB Native 1.2: `impression` (1), `viewable_mrc_50` (2), `viewable_mrc_100` (3), `viewable_video_50` (4), `audible_video_complete` (500). `audible_video_complete` is distinct from `viewable_video_50` — the former is 100% completion with audio on; the latter is 50% pixels for ≥2 seconds with audio on. Meaningful on non-VAST video (Meta Reels, YouTube Shorts, TikTok Spark) where audible-complete is measured but VAST `` isn't the wire format. + +**Negative-fixture coverage**: 10 new test vectors for `pixel_tracker` in `tests/canonical-negative-fixtures.test.cjs` covering valid shapes (impression img / impression js / viewable_mrc_50 / click / audible_video_complete / custom-with-name) and the rejection paths (custom without `custom_event_name`, non-custom with `custom_event_name`, invalid event enum, invalid method enum). Total negative-fixture count goes from 28 → 38. + +**Other adopter-facing additions:** + +- `ProductFormatDeclaration.seller_preference: "preferred" | "accepted" | "discouraged"` — soft routing hint on multi-format products. +- `placement-definition.json format_options[]` (capability_id reference OR inline) with same-file resolution scope. +- Convention lint at `tests/canonical-format-conventions.test.cjs` enforces: object-form `canonical:`, array-form `v1_format_ref[]`, size-mode mutex, AAO-mirror URL convention, slot/param consistency. + +**Round-2 adopter feedback (Pia Malovrh / Nastassia Fulconis, 2026-05-18 Slack):** + +- **`native_in_feed` canonical added.** 13th `format_kind` covering IAB OpenRTB Native 1.2 in-feed native ads, content-recommendation widgets (Taboola, Outbrain, Yahoo Native, AdMob Native), and publisher in-feed sponsored placements without catalog dependency. Slots map 1:1 to IAB Native asset types (`title`, `body_text`, `main_image`, `icon`, `cta`, `advertiser_name`, `sponsored_label`, `landing_page_url`, plus renderer-fired `pixel_tracker` trackers). Routing answer: buyer agents reading `native_in_feed` know to assemble title+image+body+CTA; reading `sponsored_placement` know to attach a catalog feed. Defaults to non-experimental (IAB Native 1.2 contract is well-established). +- **`sponsored_placement` narrowed.** Canonical is now normatively catalog-keyed retail-media ONLY: REQUIRES `source_catalog` slot. Schema description explicitly excludes IAB in-feed native, content-recommendation, PMax-style algorithmic surfaces, and single-image/video creative — those route elsewhere. Earlier broader framing failed buyer-agent routing (buyer reading `sponsored_placement` couldn't disambiguate Amazon SP from Taboola). +- **`scenes` → `video_brief` rename.** Renamed `static/schemas/source/creative/scenes.json` → `creative/video-brief.json` (`$id: /schemas/creative/video-brief.json`), wrapper field `scenes[]` → `segments[]`, per-segment `description` → `prompt`. The shape was always a structured generation brief (no camera direction, shot type, mood, or reference attachments) — the rename calls it that. asset_group_id `scenes` → `video_brief` (with `scenes`, `storyboard`, `shot_brief` as aliases). Distinct from `creative_brief` (free-form text); `video_brief` is the structured timed-prompt form. Buyers wanting visual-direction surface still attach `reference-asset.json` with `purpose: storyboard`. +- **`seller_preference` semantics clarified.** Schema description and `ProductFormatDeclaration` description now state normatively that `format_options[]` is the closed set of accepted formats for a product — sellers MUST reject `create_media_buy` requests targeting any `format_kind` outside that set. `seller_preference` is a soft ranking hint WITHIN the accepted set, NOT an enforcement axis. There is intentionally no `required` enum value; the closed-set rule already handles "this is the only format that works" (list one entry → that's the set). Pia's "won't work vs please don't" distinction is resolved structurally rather than via enum proliferation. +- **Registry two-tier boundary documented; platform-specific IDs removed.** Dropped `youtube_video_id` and `pin_id` from `asset-group-vocabulary.json`. Added normative paragraph: the canonical vocabulary is the IAB-aligned portable tier; platform-specific asset identifiers (TikTok video IDs, Snap attachment IDs, Meta Advantage+ creative IDs, etc.) belong on the canonical's `platform_extensions[]` (URI+digest reference to the platform's extension schema). Earlier draft set precedent for every platform's identifier vocabulary leaking into the canonical registry — explicitly reversed before GA. + +**Resolves** #4148 (canonical-formats vocabulary), #4620 (publisher-scoped catalogs), #4652 (.adcp placeholder cleanup), #4689 (catalog generative deannotation). Coordinated with adcp-client #1815 (SDK v1↔v2 projection) and adcp-go (catalog consumer). diff --git a/.changeset/v2-phase1-vocab-scenes-zip.md b/.changeset/v2-phase1-vocab-scenes-zip.md new file mode 100644 index 0000000000..675cdd892d --- /dev/null +++ b/.changeset/v2-phase1-vocab-scenes-zip.md @@ -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. diff --git a/docs/building/by-layer/L1/security.mdx b/docs/building/by-layer/L1/security.mdx index d7c251bfa1..2b3fe3ebec 100644 --- a/docs/building/by-layer/L1/security.mdx +++ b/docs/building/by-layer/L1/security.mdx @@ -1583,7 +1583,7 @@ export async function verifyAdcpRequestSignature(req: Request, ctx: { const { jwk } = await ctx.resolveJwk(parsed.keyid!); // throws _key_unknown // 8: key purpose const j = jwk as any; - if (j.use !== "sig" || !Array.isArray(j.key_ops) || !j.key_ops.includes("verify") || j.adcp_use !== "request-signing") { + if (j.use !== "sig" || !Array.isArray(j.key_ops) || !j.key_ops.includes("verify") || j.example_use !== "request-signing") { throw new RequestSignatureError("request_signature_key_purpose_invalid"); } // 9: revocation (BEFORE crypto verify) diff --git a/docs/creative/asset-types.mdx b/docs/creative/asset-types.mdx index 74ec9e9123..8e10fe260b 100644 --- a/docs/creative/asset-types.mdx +++ b/docs/creative/asset-types.mdx @@ -4,11 +4,40 @@ description: "AdCP asset types define standardized properties for images, video, "og:title": "AdCP — Asset Types" --- +> **Canonical-formats readers**: this page describes asset types and their payload shapes — the same in v1 and the canonical-formats path. For how assets map to canonical-format slots via `asset_group_id`, see [canonical-formats](/docs/creative/canonical-formats) and the [migration guide](/docs/creative/canonical-formats-migration). v1 uses `asset_id` + `asset_role`; canonical formats use `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` | +| `pixel_tracker` | Renderer-fired HTTP tracker (image pixel or JS include) for any web-rendered canonical. `event` (impression / viewable_mrc_* / viewable_video_50 / audible_video_complete / click / custom) × `method` (img / js). Maps to IAB OpenRTB Native 1.2 `imptrackers[]` / `jstracker` / `eventtrackers[]` / `link.clicktrackers[]` event-type registry (types 1, 2, 3, 4, 500) | `/schemas/core/assets/pixel-tracker-asset.json` | +| `object` | Structured object (image_carousel `cards`, `video_brief` 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: @@ -186,6 +215,8 @@ In format requirements, `role` declares what the URL slot is for: `role` enum: `clickthrough`, `landing_page`, `impression_tracker`, `click_tracker`, `viewability_tracker`, `third_party_tracker`. +> **For canonical-formats v2: prefer `pixel_tracker` over `url` + `url_type: tracker_pixel`** for renderer-fired measurement trackers on any web-rendered canonical (image, html5, image_carousel, responsive_creative, sponsored_placement, native, video_hosted non-VAST events, audio_hosted non-DAAST events). The legacy `url` shape remains valid for VAST/DAAST `` URLs and for backward compatibility; new authoring SHOULD use the typed `pixel_tracker` form because it carries the event + method semantics that bare URLs lose. See the dedicated section below. + #### Other URL-asset-requirements properties - `protocols`: Allowed schemes (`https`, `http`) @@ -397,6 +428,36 @@ Audio-side analogue of `vast_tracker`: a single URL bound to a DAAST `TrackingEv - `offset`: Required for `daast_event: "progress"` (DAAST 1.1 §3.2.4.3). Same format as VAST 4.2 `Tracking@offset`. Negative offsets are NOT permitted. - `target`: Which DAAST creative element scopes this tracker — `linear` (default, DAAST 1.1 §3.2.1.7) or `companion` (DAAST 1.1 §3.2.2.7, where the only valid event is `creativeView`). DAAST has no `` element — audio is linear or accompanied by a visual companion. +### Pixel Tracker Asset + +Renderer-fired HTTP tracker — image pixel or JavaScript include — bound to a measurement event. Applies to any **web-rendered** canonical format (image, html5, image_carousel, responsive_creative, sponsored_placement, native_*, plus non-VAST/DAAST events on video_hosted / audio_hosted). Format-specific tracker primitives (`vast_tracker`, `daast_tracker`) remain the right shape for VAST/DAAST event semantics; `display_tag` is opaque (third-party served). + +**Example:** +```json +{ + "asset_type": "pixel_tracker", + "event": "impression", + "method": "img", + "url": "https://measurement.example.com/imp?cb={CACHEBUSTER}&cid={CREATIVE_ID}" +} +``` + +**Properties:** +- `event`: `impression` | `viewable_mrc_50` | `viewable_mrc_100` | `viewable_video_50` | `audible_video_complete` | `click` | `custom`. Event enum mirrors IAB OpenRTB Native 1.2 event-type registry (types 1, 2, 3, 4, 500). `audible_video_complete` is distinct from `viewable_video_50` — the former is full-completion with audio on; the latter is 50% pixels for ≥2 seconds with audio on. Meaningful on non-VAST video formats (Meta Reels, YouTube Shorts, TikTok Spark); VAST formats use `vast_tracker` with `vast_event: complete` instead. +- `method`: `img` (fired as `` pixel) or `js` (fired as `