webhooks.mdx states a MUST NOT that no published JSON Schema encodes, at any
version I can find. An implementer validating against the schema and an
implementer reading the docs reach opposite conclusions about the same payload.
The prose
docs/building/by-layer/L3/webhooks.mdx:271 (at head):
For delivery-report data events such as scheduled, final, delayed, adjusted, and window_update, notification_id is absent by design; dedupe the transport event with idempotency_key. The aggregated_totals field is API-only for get_media_buy_delivery responses and must not be emitted in reporting webhook result payloads.
The schema
media-buy/media-buy-delivery-webhook-result.json, checked at three versions:
| version |
additionalProperties |
declares aggregated_totals |
not / allOf guard |
| 3.1 |
true |
no |
none |
3.1.20 (latest_stable) |
true |
no |
none |
| 3.2.0-rc.1 |
true |
no |
none |
With additionalProperties: true and no negative constraint, a result payload
carrying aggregated_totals validates cleanly. There is nothing in the
schema for a conformance checker to fire on.
The generated SDK agrees with the schema, not the docs
The Python SDK's generated model behaves the same way, which matters because it
is what most implementers actually touch:
>>> from adcp.types import MediaBuyDeliveryWebhookResult as M
>>> M.model_config["extra"]
'allow'
>>> "aggregated_totals" in M.model_fields
False
>>> m = M.model_validate({..., "aggregated_totals": {"impressions": 1, "spend": 1.0}})
>>> "aggregated_totals" in m.model_dump()
True
So the model accepts the forbidden field and round-trips it into its own
output. A sender that builds its payload through the SDK — the path you would
most want to be safe — gets no signal at all, and will happily re-emit the field
it was handed.
Three artifacts, two answers: the docs forbid it; the schema and the SDK permit
it and preserve it.
Why this is worth resolving rather than leaving to convention
The two artifacts are not equally consumable. Schemas are executable: they drive
validators, code generation, and conformance suites. Prose is not. When a
normative requirement exists only in prose, every implementer has to re-derive it
by reading, and no tool can catch a regression against it — which is how a
sender emits the field for a long time while its own test suite stays green.
Our reading is that the schema is the contract and the docs and SDKs are
derivatives of it: if the schema does not encode a restriction, a payload that
satisfies the schema is conformant. On that reading this MUST NOT is
unenforceable as written, and the sentence overstates what the protocol actually
requires.
The question
Which artifact is normative here?
- The prose is right and the schema is incomplete. Then the schema should
encode it — for example "aggregated_totals": false under
propertyNames/not, or an explicit
"not": {"required": ["aggregated_totals"]} on the result schema. Happy to
send a PR if you confirm the intended shape.
- The schema is right and the prose overstates. Then :271 should read as
guidance ("aggregated_totals is API-only and SHOULD NOT be emitted...")
rather than a MUST NOT, so implementers are not held to an unenforceable rule.
Either resolution is fine from our side — the ambiguity is the problem, not the
direction.
Context
Found while adding schema-compliance grading to a seller's webhook test suite:
the compliance check accepts a payload the docs forbid, so the general check and
a field-specific assertion disagreed, and tracing that disagreement led here.
Related, and possibly the same root cause: media-buy-delivery-webhook-result.json
sets additionalProperties: true generally, so any API-only field can ride along
in a webhook result without a validator objecting. If the intent is that a webhook
result is a strict subset of the API response, that may be worth stating in the
schema more broadly than one field.
webhooks.mdxstates a MUST NOT that no published JSON Schema encodes, at anyversion I can find. An implementer validating against the schema and an
implementer reading the docs reach opposite conclusions about the same payload.
The prose
docs/building/by-layer/L3/webhooks.mdx:271(at head):The schema
media-buy/media-buy-delivery-webhook-result.json, checked at three versions:additionalPropertiesaggregated_totalsnot/allOfguardtruelatest_stable)truetrueWith
additionalProperties: trueand no negative constraint, a result payloadcarrying
aggregated_totalsvalidates cleanly. There is nothing in theschema for a conformance checker to fire on.
The generated SDK agrees with the schema, not the docs
The Python SDK's generated model behaves the same way, which matters because it
is what most implementers actually touch:
So the model accepts the forbidden field and round-trips it into its own
output. A sender that builds its payload through the SDK — the path you would
most want to be safe — gets no signal at all, and will happily re-emit the field
it was handed.
Three artifacts, two answers: the docs forbid it; the schema and the SDK permit
it and preserve it.
Why this is worth resolving rather than leaving to convention
The two artifacts are not equally consumable. Schemas are executable: they drive
validators, code generation, and conformance suites. Prose is not. When a
normative requirement exists only in prose, every implementer has to re-derive it
by reading, and no tool can catch a regression against it — which is how a
sender emits the field for a long time while its own test suite stays green.
Our reading is that the schema is the contract and the docs and SDKs are
derivatives of it: if the schema does not encode a restriction, a payload that
satisfies the schema is conformant. On that reading this MUST NOT is
unenforceable as written, and the sentence overstates what the protocol actually
requires.
The question
Which artifact is normative here?
encode it — for example
"aggregated_totals": falseunderpropertyNames/not, or an explicit"not": {"required": ["aggregated_totals"]}on the result schema. Happy tosend a PR if you confirm the intended shape.
guidance ("
aggregated_totalsis API-only and SHOULD NOT be emitted...")rather than a MUST NOT, so implementers are not held to an unenforceable rule.
Either resolution is fine from our side — the ambiguity is the problem, not the
direction.
Context
Found while adding schema-compliance grading to a seller's webhook test suite:
the compliance check accepts a payload the docs forbid, so the general check and
a field-specific assertion disagreed, and tracing that disagreement led here.
Related, and possibly the same root cause:
media-buy-delivery-webhook-result.jsonsets
additionalProperties: truegenerally, so any API-only field can ride alongin a webhook result without a validator objecting. If the intent is that a webhook
result is a strict subset of the API response, that may be worth stating in the
schema more broadly than one field.