AdCP schemas carry severalDocumentation Index
Fetch the complete documentation index at: https://agenticadvertisingorg-changeset-release-main.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
x- prefixed annotation keywords that supplement the JSON Schema vocabulary. JSON Schema validators ignore unknown x- keywords per draft-07 §6, so adding these keys is wire-compatible with any conforming validator.
This page is the canonical reference for those annotations. Codegen consumers (TypeScript / Python / Go type generators), the storyboard runner, and the AdCP SDK family read these annotations programmatically; verifiers MAY read them but the normative behavior they describe is also documented in the relevant section of security.mdx or the field’s own description.
x-status
Marks a schema or property as experimental — part of the core protocol but not yet frozen. Sellers implementing experimental surfaces declare the feature id in experimental_features on get_adcp_capabilities. See Experimental Status for the full graduation policy.
"experimental". The keyword is omitted on stable surfaces.
x-adcp-validation
Lifts structured normative constraints out of prose descriptions into a machine-readable shape. Storyboard runners and SDK validators consume the structured rules; codegen consumers can ignore the annotation and read the human description.
The keyword is most useful for fields whose description currently carries a “MUST be present when…” or “MUST equal the host of…” clause that the storyboard runner cannot enforce by parsing English.
Shape
Sub-keys
| Key | Type | Purpose |
|---|---|---|
trust_root | boolean | Field is load-bearing for signature verification; verifiers MUST treat as authoritative. |
required_when | object wrapping any_of / all_of | Storyboard-enforced required-when rules (3.x). The object has exactly one of any_of (OR-joined) or all_of (AND-joined), each containing an array of leaf conditions. Each leaf is one of: { "field": "...", "non_empty": true }, { "field": "...", "equals": <value> }, { "field": "...", "any_subfield_present": true }. The wrapping object mirrors JSON Schema’s anyOf/allOf precedent so tooling readers can reuse familiar boolean-combinator semantics. Bare arrays are NOT accepted — always wrap. |
schema_required_when | condition | When the rule promotes from storyboard-enforced to schema-required. Typically keyed on adcp.supported_versions matching a version pattern via any_item_matches_pattern (e.g., "^4\\." for the 4.0 cutover and any 4.x patch). |
forbidden_when | object wrapping any_of / all_of | Inverse of required_when. The field MUST be absent (or false/empty, depending on type) when the wrapped condition holds. Same leaf shape as required_when. Use for fields whose presence is mutually exclusive with another posture. |
disjoint_with | string (dotted path) or array of dotted paths | Item-level mutual exclusion: no value in this field’s array MAY appear in any of the named arrays. Storyboard runners assert set-disjointness on each. Example: request_signing.warn_for carries disjoint_with: "request_signing.required_for" because an operation can be in one or the other, never both. |
subset_of | string (dotted path) | Item-level subset constraint: every value in this field’s array MUST also appear in the named array. Example: request_signing.required_for carries subset_of: "request_signing.supported_for" — an operation can’t be required without being supported. |
verifier_constraints | object | Free-form key-value map of verifier-side rules that don’t fit the structured sub-keys above. Keys are normative (e.g., agent_url_match: "byte_equal"); the storyboard runner enforces these against test vectors. Prefer the structured sub-keys (required_when, forbidden_when, disjoint_with, subset_of) when they fit; reach for verifier_constraints only for one-off rules that don’t generalize. |
distinct_from | string (dotted path) | Names another field that has a similar shape but different semantics, to defuse name confusion (e.g., identity.brand_json_url distinct from sponsored_intelligence.brand_url). Verifiers MUST NOT substitute one for the other. |
spec | string (relative path with anchor) | Pointer to the normative section in the docs that defines the field’s full semantics. Always required when other sub-keys are present. |
Conformance
- Validators MUST ignore unknown sub-keys for forward-compatibility (the schema may add new entries in a minor release).
- The storyboard runner consumes
required_when,schema_required_when, andverifier_constraintsto generate test cases per release; runners that don’t yet recognize a sub-key MUST skip it and emit an “unrecognized validation rule” warning. - Codegen consumers (TypeScript / Python / Go type generators) MAY surface
x-adcp-validation.specas a@seeJSDoc link but otherwise treat the annotation as opaque.
Current usage
Representative usage:| Field | Sub-keys used | Rule |
|---|---|---|
identity.brand_json_url | trust_root, required_when, schema_required_when, verifier_constraints, distinct_from | Trust-root pointer for signing-key discovery; required-when tied to signing posture; schema-required in 4.0; distinct from sponsored_intelligence.brand_url. See security.mdx §Discovering an agent’s signing keys. |
identity.key_origins | verifier_constraints (purpose_anchoring) | Every purpose listed MUST have a corresponding signing posture declared elsewhere on the response. Cross-field rule. See security.mdx §Origin separation. |
request_signing.required_for | subset_of | Every operation listed MUST also appear in supported_for — an operation can’t be required without being supported. |
request_signing.warn_for | disjoint_with, subset_of | An operation MUST NOT appear in both warn_for and required_for. Every operation listed MUST also appear in supported_for. |
webhook_signing.supported | verifier_constraints (must_equal_when) | When the seller advertises mutating-webhook emission (media_buy.reporting_delivery_methods includes webhook OR media_buy.content_standards.supports_webhook_delivery: true), supported MUST be true. Closes a downgrade vector. |
wholesale_feed_webhooks.event_types | verifier_constraints (wholesale_feed_webhook_capability_consistency) | product.* event types require wholesale get_products; signal.* event types require wholesale get_signals; wholesale_feed.bulk_change requires at least one declared wholesale repair path and must name only a repairable feed family. |
get_products.wholesale_feed_version / get_signals.wholesale_feed_version | verifier_constraints (required_for_wholesale_request) | Version tokens are required on wholesale read responses, but the shared response schemas cannot infer the request’s buying_mode / discovery_mode from the response body alone. |
adcp.idempotency— the discriminatedoneOfalready requiresreplay_ttl_secondsin the supported branch and forbids it in the unsupported branch.webhook_signing.algorithms— theenum: ["ed25519", "ecdsa-p256-sha256"]on each item already enforces the allowlist.
x-adcp-hoist
Build-time directive that marks a source schema as a canonically shared type. The schema bundler hoists every inline occurrence into a single root $defs entry and replaces inline copies with $ref pointers; the directive itself is stripped from bundled output. Wire-irrelevant — validators MUST ignore it (draft-07 §6 unknown-keyword semantics) and conforming consumers SHOULD NOT observe it on bundled artifacts.
Why opt-in for complex objects
Pure enums hoist automatically (seehoistDuplicateInlineEnums) because merging two structurally-identical enums is semantics-preserving. Complex objects are different — structural identity ≠ semantic identity. BriefAsset (proposed creative spec) and VASTAsset (delivered video creative) currently share fields but represent different lifecycle concepts; auto-merging them would create cross-tool coupling the source schemas don’t express, and would be hard to unwind once SDKs codegen against the merged type. x-adcp-hoist makes the share-or-split decision deliberate per schema.
Bundler behavior
- Hoists at any occurrence count (≥1). The directive declares intent — “this is a canonical named type” — so adding a second reference later never changes the codegen surface.
titleis required. Missing or empty title → build-time error. The directive is meant to be deliberate.- Same title + different shape is a build-time error. Two marked schemas authored with the same
titlebut distinct fields would otherwise silently suffix one toFoo2, defeating the directive’s “canonical name” guarantee. - Collision with a pre-existing
$defskey is suffixed (PriceBlock2), matching the convention used by the pure-enum hoist. - Stripped from bundled output — both from the canonical
$defsentry and from any stray marker that was authored inside a pre-existing$defsblock.
SDK / codegen impact
Addingx-adcp-hoist to a previously-inlined source schema is wire-compatible (the bundled schema still validates the same payloads) but is a codegen-shape change: TypeScript / Python / Go type generators that previously emitted an anonymous inline type (often Foo1, Foo2, …) will now emit a single named type. SDK adopters maintain rename aliases per their own deprecation policy — see adcp-client#942 for the client-side rename/alias tracking.
Conformance
- Validators MUST ignore
x-adcp-hoistper draft-07 §6 (unknown keywords are tolerated). The directive has no wire semantics. - Source-tree consumers (third parties that dereference
static/schemas/source/...directly rather than the bundled artifacts) MUST treatx-adcp-hoist: trueas a no-op annotation. The schema’s content is the contract. - Bundlers other than
scripts/build-schemas.cjsMAY honor the directive or ignore it; a bundler that ignores it produces a wire-compatible bundle with un-deduped inline copies.
History
Future extensions
Newx-adcp-* keywords are added in minor releases. Consumers MUST tolerate unknown x- keywords without erroring. The convention reserves the x-adcp- namespace; vendor-specific or deployment-specific annotations SHOULD use a vendor-specific prefix (e.g., x-yourorg-) to avoid collision.