Gap
format.assets[] describes the asset slots a format supports, with one boolean discriminator: required: true | false.
Per format.json:24-27:
`required: false` means "optional — enhances but not mandatory"
But this conflates two semantically distinct cases:
- Optional input — buyer MAY provide it (e.g. an optional CTA in a display banner)
- Output-only — buyer CANNOT provide it; the agent generates it via
build_creative and returns it in the response manifest
The creative-manifest.json schema is bidirectional — the same shape is used for input requests (build_creative.creative_manifest) AND output responses (BuildCreativeSuccess.creative_manifest). Both reference format.assets[] for asset_id keying per creative-manifest.json:14 ("each key MUST match an asset_id from the format's assets array").
Concrete impact
A worked creative-template adapter declares input slots image / headline / cta / click_through (all required) and produces a build_creative output keyed under serving_tag of asset_type: 'html'. To satisfy creative-manifest.json:14, the adapter must declare serving_tag in format.assets[] — but required: true would (incorrectly) tell buyers "you must provide this in your build_creative request," and required: false understates the constraint (buyers can't provide it; rejecting a serving_tag input is a conformance ambiguity).
See examples/hello_creative_adapter_template.ts and examples/hello_creative_adapter_ad_server.ts in adcontextprotocol/adcp-client (PR #1511) — both adapters now declare an output slot with required: false because that's the closest legal expression in 3.0.5, but PE flagged this as an ambiguity that belongs upstream.
Proposal
Add one of:
Option A — produced_by enum (preferred — extensible)
format.assets[]:
- asset_id: 'serving_tag'
asset_type: 'html'
required: false
produced_by: 'build_creative' # or 'render_time' | 'buyer'
produced_by: 'buyer' (default) means the buyer provides the asset in input requests. produced_by: 'build_creative' means the agent generates it; buyers MUST NOT include it in input requests. produced_by: 'render_time' reserves a path for assets resolved at impression-serving time (e.g. dynamic creative optimization output).
Option B — output_only: true flag (simpler — boolean)
format.assets[]:
- asset_id: 'serving_tag'
asset_type: 'html'
output_only: true # or false (default — buyer-provided)
Mutually exclusive with required semantically: output_only: true implies the slot doesn't apply to input validation.
Prior art
OpenRTB Native 1.2 §4.2 distinguishes `required` (bidder-supplied) from publisher-rendered fields. IAB OpenRTB 2.6 separates request-side from response-side fields explicitly. The same shape applies here: an asset slot's role differs between the buyer's request to build_creative and the agent's response.
Why now
@adcp/sdk PR #1511 just landed the spec-aligned output slot declaration in two worked creative adapters (template + ad-server) using required: false. The PR's PE review flagged the conflation as a real spec gap — captured here so adopters reading those adapters have a tracked issue to follow when the conflation is resolved.
Cross-references
Gap
format.assets[]describes the asset slots a format supports, with one boolean discriminator:required: true | false.Per
format.json:24-27:But this conflates two semantically distinct cases:
build_creativeand returns it in the response manifestThe
creative-manifest.jsonschema is bidirectional — the same shape is used for input requests (build_creative.creative_manifest) AND output responses (BuildCreativeSuccess.creative_manifest). Both referenceformat.assets[]for asset_id keying percreative-manifest.json:14("each key MUST match an asset_id from the format's assets array").Concrete impact
A worked creative-template adapter declares input slots
image / headline / cta / click_through(allrequired) and produces a build_creative output keyed underserving_tagofasset_type: 'html'. To satisfy creative-manifest.json:14, the adapter must declareserving_taginformat.assets[]— butrequired: truewould (incorrectly) tell buyers "you must provide this in your build_creative request," andrequired: falseunderstates the constraint (buyers can't provide it; rejecting aserving_taginput is a conformance ambiguity).See
examples/hello_creative_adapter_template.tsandexamples/hello_creative_adapter_ad_server.tsin adcontextprotocol/adcp-client (PR #1511) — both adapters now declare an output slot withrequired: falsebecause that's the closest legal expression in 3.0.5, but PE flagged this as an ambiguity that belongs upstream.Proposal
Add one of:
Option A —
produced_byenum (preferred — extensible)produced_by: 'buyer'(default) means the buyer provides the asset in input requests.produced_by: 'build_creative'means the agent generates it; buyers MUST NOT include it in input requests.produced_by: 'render_time'reserves a path for assets resolved at impression-serving time (e.g. dynamic creative optimization output).Option B —
output_only: trueflag (simpler — boolean)Mutually exclusive with
requiredsemantically:output_only: trueimplies the slot doesn't apply to input validation.Prior art
OpenRTB Native 1.2 §4.2 distinguishes `required` (bidder-supplied) from publisher-rendered fields. IAB OpenRTB 2.6 separates request-side from response-side fields explicitly. The same shape applies here: an asset slot's role differs between the buyer's request to
build_creativeand the agent's response.Why now
@adcp/sdk PR #1511 just landed the spec-aligned output slot declaration in two worked creative adapters (template + ad-server) using
required: false. The PR's PE review flagged the conflation as a real spec gap — captured here so adopters reading those adapters have a tracked issue to follow when the conflation is resolved.Cross-references
creative_templatestoryboard (separate concern; uses output slot)