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
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:
Already enforced natively by JSON Schema and excluded from migration:
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-open-payload
Classifies fields that look open-ended to SDK generators. The annotation is documentary and non-validating; the field’s JSON Schema still controls wire validation.
Allowed authoring values:
false is reserved for a future structured-but-extension-tolerant marker. Source schemas MUST NOT set x-adcp-open-payload: false until the repository defines at least one canonical use site and the generator contract for that value.
For mixed fields, the annotation applies only to the object or decoded-JSON payload arm. Scalar arms still follow their declared schema. For example, an upstream recorded body may be a decoded JSON object when the content type is JSON-shaped and a string otherwise; x-adcp-open-payload: true marks the decoded JSON payload as intentionally open, not the string arm as an object model.
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.