Repository navigation
[Comment] § 2 — The draft does not say what a parser does with a key it does not define #33
Description
Which section?
2 — Object shape
Field(s) involved
whole document; § 2.2 immutability at agreed; § 2.1 representations; external_references[] (§ 5.10)
Your comment
There is no sentence anywhere stating whether a document carrying a key the specification does not define is valid, and if so what a reader does with that key.
Two parsers will therefore behave in opposite ways, and both are conformant under the current text:
- A strict parser rejects the document. A vendor key anywhere stops the deal.
- A lenient parser ignores the key and forwards the rest. The key is silently dropped in transit and the party that wrote it never learns.
The second outcome contradicts § 2.2. The agreed record is "self-contained and immutable", but if one implementation stores a vendor key and another strips it, two copies of the same agreed proposal are not the same bytes, and nothing in the specification says which is correct.
The pressure is already visible in this comment period. #26 adds a seller-internal id that buyers "never resolve". #27 documents two new external_references[].role values. #29's manifest example carries a spec_version key with no home in the object. These went through change requests. The keys that will not, because they are specific to one ad server or one integration, will be added anyway. external_references[] is not a home for them: it is derived, shaped as { system, namespace, id, role }, and cannot hold a structured or seller-set value.
The question needs answering in the prose before the schema is written, because the schema's additionalProperties setting is this rule in machine form. Whoever writes the schema will decide it either way; the body should decide first.
Either answer is workable:
-
Strict. "A document carrying a key this specification does not define is invalid." Simple, and consistent with turning every new need into a change request. The cost is that no implementation can carry anything the working group has not blessed, and vendor-specific keys end up in
content_detailorspecificationsas free text. -
Reserved container. An
extobject on the proposal and on each line item, with reverse-DNS namespaced keys, that parsers MUST preserve unchanged and that is part of the frozen record atagreed; undefined keys anywhere else are invalid. This is the convention OpenRTB, AdCOM and OpenDirect already use, and every implementer this draft is written for knows it. It costs two optional rows and one paragraph.
We would support either. What the draft cannot do is leave the question open, because then the answer is chosen separately by each parser, and the agreed record stops being one record.
Concrete scenario
A seller's ad server needs its internal placement code on each line item so the agreed proposal can be matched to the booked line without a lookup table. The seller adds a top-level key:
line_item_id: "business-daily-display"
channel: display
placement_code: "BD-ROS-300x250-Q4" # not in the specificationThe buyer's agent fetches the Full Representation through an SSP that re-serialises proposals. The SSP's parser is strict and rejects the document; the line item never reaches the buyer. A second SSP's parser is lenient and drops the key. The buyer composes through the second SSP, both parties reach agreed, and the seller's stored record carries placement_code while the buyer's does not. A reconciliation tool later compares the two agreed records and finds them different, with nothing in the specification to say which one is right.
Under answer 1 the seller learns at publication that the key is invalid and puts the code somewhere else. Under answer 2 the seller writes ext: { com.examplepub.placement_code: "BD-ROS-300x250-Q4" }, both SSPs preserve it, and the two agreed records are identical. Under the current text the seller learns at reconciliation, which is the only outcome the specification should rule out.
You are commenting as
AdTech Provider
Before you submit
- I have read CONTRIBUTING.md and agree my contribution may be published under this repository's licence.
- This comment contains no confidential or commercially sensitive information.
Activity
Sirajmx commentedon Oct 21, 2026
Thank you for filing this. It has been triaged and labelled for the working group, and will be considered at adjudication after the comment period closes on 22 October 2026.
jaanijuk commentedon Oct 28, 2026
Implementation evidence for this issue, under draft 1 at
44dfaaf. #134 has the full input and run details.We sent one DOOH line item carrying six top-level fields the draft doesn't define to an unreleased seller-agent branch (
IABTechLab/seller-agent,feat/openproposal-mappingate77022e). Its line-item model uses Pydantic v2's default handling of undeclared fields, which ignores them; it doesn't setextra="forbid". The model accepted the line item without a validation error, and all six fields were missing from its output. None was kept underext. The results were byte-identical across three runs in two environments.Checked separately, with Python
jsonschema4.26.0, against an independent, unofficial JSON Schema generated from the draft (KonstantinMirin/openproposal-schemaat159a254, as-written build; see #121), the same input was rejected, with each of the six fields treated as a property the line item doesn't define. That's the behavior of this schema's validation rules, which close every object the draft defines. Where the draft is silent, those rules are the schema author's choices, not requirements of the draft. So the split this issue describes shows up with one input: one tool trims it without an error, and the other's rules reject it, each because of how it's configured rather than anything the draft says.An
extcontainer on its own doesn't settle it. The same branch's GAM mappings write anext.gamobject for ad-server details, so the branch already has a place for extensions. The six DOOH fields still weren't moved intoext; they were dropped. And under the strict answer as this issue words it, the branch's own GAM output would be invalid, because the draft defines noext.We support the reserved-container answer, which #131 drafts as a change request:
exton the proposal and on each line item, with namespaced keys, preserved unchanged and part of the frozen record atagreed. Rejecting undefined keys outsideextis also our preferred policy, though our test alone doesn't establish it. Preservation isn't enough on its own, though. Five additions:channel, provided it doesn't assent to that line item. It MUST NOT keep a line item after removing fields from it.extterm doesn't mean the receiver understands it or can fulfill it, so a line item could reachagreedon a term one side can't honor. Anyextterm that's binding on the agreement should need explicit acceptance beforeagreed; otherwise the line item is rejected. Informational extensions, such as the ad-server details inext.gam, wouldn't need it; for those, ignoring unknown keys, as [Change] § 2, § 4.1, § 5.1 — Add #33's ext container: keyed by domain, ignored when unknown, kept in the agreed record #131 proposes, is right. One mechanism is a list ofextkeys the receiver MUST understand, like thecritheader parameter in JWS (RFC 7515, § 4.1.11).ext. A digest such as SHA-256 over the RFC 8785 canonical JSON form, which [Change] § 4.1 — Define record_digest and assent[]; defer signer identity to the AAMP profile #63 drafts asrecord_digest, shows both parties hold the same canonical content, though not necessarily the same original bytes. It can't show they read it the same way, which is why point 3 matters.extmust come back withextunchanged. Under the policy we prefer, one carrying an undefined key outsideextmust be rejected.