Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dooh-placement-attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"adcontextprotocol": minor
---

Add DOOH structured selling-unit fields to placements: `dooh_placement_attributes` (slot_duration_seconds, loop_duration_seconds, screen_resolution, motion) and `identifiers[]` on both placement.json and placement-definition.json. Define deterministic publisher/product inheritance, post-merge slot-to-loop validation, versioned OpenOOH identifiers, and canonical-format authority. Add the `dooh-motion-type` enum and supersede pricing-layer loop_duration_seconds in flat-rate-option.json.
3 changes: 1 addition & 2 deletions docs/media-buy/advanced-topics/pricing-models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,6 @@ Revenue share is channel-independent. Affiliate is the immediate use case, but s
"daypart": "morning_commute",
"duration_hours": 4,
"sov_percentage": 25,
"loop_duration_seconds": 15,
"estimated_impressions": 120000
}
}
Expand All @@ -466,7 +465,7 @@ Revenue share is channel-independent. Affiliate is the immediate use case, but s

**DOOH parameters** (`parameters.type: "dooh"`):
- `sov_percentage`: Guaranteed share of voice as a percentage (0-100)
- `loop_duration_seconds`: Duration of ad loop rotation in seconds
- `loop_duration_seconds`: Deprecated compatibility copy of the placement's `dooh_placement_attributes.loop_duration_seconds`. New integrations read the placement-level value; when both are present they must agree. Offers with different loop durations use distinct placements or products rather than pricing-option-specific copies.
- `min_plays_per_hour`: Minimum guaranteed plays per hour
- `venue_package`: Named collection of screens
- `duration_hours`: Duration of the slot in hours (e.g., 24 for a full-day takeover)
Expand Down
19 changes: 19 additions & 0 deletions docs/media-buy/product-discovery/media-products.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,25 @@ The field is an array because a sellable product can aggregate multiple surfaces

This is a discovery signal, not a verification claim. Buyers can filter for products that can satisfy a requested surface with `get_products.filters.social_placement_surfaces`, but sellers should not return mixed, non-targetable bundles unless they can constrain delivery to the requested surface during planning or purchase.

#### DOOH placement attributes

Digital out-of-home placements can declare structured selling-unit metadata via `dooh_placement_attributes` at the placement level (both in product placements and in `adagents.json` placement definitions). These fields describe the physical screen and ad-loop characteristics that buyers need for creative production and share-of-voice calculations:

| Field | Type | Meaning |
|---|---|---|
| `slot_duration_seconds` | integer | Duration of one ad slot in seconds (e.g., 10, 15, 30) |
| `loop_duration_seconds` | integer | Duration of the full ad loop rotation in seconds. Buyers derive nominal slot share as `slot_duration_seconds / loop_duration_seconds`. This is the canonical source; the pricing-layer field in `flat-rate-option` is superseded |
| `screen_resolution` | object | Physical screen resolution (`{ width, height }` in pixels). Buyers derive aspect ratio from `width / height` |
| `motion` | string | Motion capability of the screen: `full_motion` (video), `partial_motion` (animated stills), or `static` (images only) |

All fields are optional. A single screen/frame placement can include all fields; a package or network placement should include only fields that are uniform across the included inventory. After publisher and product declarations are resolved, `slot_duration_seconds` MUST NOT exceed `loop_duration_seconds`.

These are inventory facts, not an alternative creative contract. Effective canonical `format_options` remains authoritative for accepted dimensions, durations, asset types, and codecs. A physical screen can therefore report a larger `screen_resolution` than the content region declared by its formats, and a full-motion screen can still offer a static-only product.

For `kind: "publisher_ref"`, resolve the publisher placement before applying product detail. Product-level `slot_duration_seconds` and `loop_duration_seconds` override publisher defaults for that specific offer; omitted values inherit. `screen_resolution` and `motion` are intrinsic publisher facts, so repeated product values MUST match the publisher declaration. Treat mismatches and an invalid effective slot-to-loop ratio as conformance errors rather than silently choosing one source.

Placements can also carry `identifiers[]` using the same `{ type, value }` shape as property identifiers. DOOH placements commonly use `venue_id`, `screen_id`, and `openooh_venue_type` identifier types. Externally governed IDs should be authority-prefixed (e.g., `geopath:30961`). OpenOOH classifications include the taxonomy version (`openooh-1.1:20501`), since a bare category number does not identify which revision defined it. For publisher references, union publisher and product identifiers by exact `(type, value)`; product omission does not remove a publisher identifier.

#### Format precedence with placements

Product-level `format_options` define the creative formats accepted by the product as a whole. Placement-level `format_options`, whether returned inline on the product placement or inherited from a public publisher placement declaration, only narrow that product-wide set for the specific placement. Deprecated `format_ids` may appear only as a 3.x compatibility projection of the same declarations.
Expand Down
65 changes: 64 additions & 1 deletion static/schemas/source/core/canonical-placement.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,70 @@
"video_placement_types": { "type": "array", "items": { "$ref": "/schemas/enums/video-placement-type.json" }, "minItems": 1, "uniqueItems": true },
"audio_distribution_types": { "type": "array", "items": { "$ref": "/schemas/enums/audio-distribution-type.json" }, "minItems": 1, "uniqueItems": true },
"sponsored_placement_types": { "type": "array", "items": { "$ref": "/schemas/enums/sponsored-placement-type.json" }, "minItems": 1, "uniqueItems": true },
"social_placement_surfaces": { "type": "array", "items": { "$ref": "/schemas/enums/social-placement-surface.json" }, "minItems": 1, "uniqueItems": true }
"social_placement_surfaces": { "type": "array", "items": { "$ref": "/schemas/enums/social-placement-surface.json" }, "minItems": 1, "uniqueItems": true },
"identifiers": {
"type": "array",
"description": "Optional external inventory identifiers for this placement. Externally governed values should be authority-prefixed; seller-local values are scoped by the surrounding publisher namespace. For publisher_ref placements, the effective set is the union of publisher and product declarations, de-duplicated by exact (type, value).",
"items": {
"type": "object",
"properties": {
"type": { "$ref": "/schemas/enums/identifier-types.json" },
"value": { "type": "string" }
},
"required": ["type", "value"],
"additionalProperties": true
},
"uniqueItems": true,
"minItems": 1
},
"dooh_placement_attributes": {
"type": "object",
"description": "DOOH screen and scheduled-loop facts. These fields do not define creative acceptance, which is governed exclusively by effective canonical format_options. For publisher_ref placements, product slot and loop values override publisher defaults while repeated screen_resolution and motion values must equal the publisher facts.",
"properties": {
"slot_duration_seconds": {
"type": "integer",
"minimum": 1,
"description": "Scheduled duration of one ad slot in seconds; not the creative-duration contract."
},
"loop_duration_seconds": {
"type": "integer",
"minimum": 1,
"description": "Duration of the full ad loop rotation in seconds and the canonical source for loop duration."
},
"screen_resolution": {
"type": "object",
"description": "Physical screen resolution; canonical format dimensions remain authoritative for creative acceptance.",
"properties": {
"width": { "type": "integer", "minimum": 1 },
"height": { "type": "integer", "minimum": 1 }
},
"required": ["width", "height"],
"additionalProperties": false
},
"motion": {
"$ref": "/schemas/enums/dooh-motion-type.json",
"description": "Physical motion capability, not an accepted-format declaration."
}
},
"x-adcp-validation": {
"verifier_constraints": {
"slot_fits_loop": {
"left_path": "slot_duration_seconds",
"operator": "less_than_or_equal",
"right_path": "loop_duration_seconds",
"evaluate_after": "publisher_ref_resolution"
},
"publisher_ref_resolution": {
"override_fields": ["slot_duration_seconds", "loop_duration_seconds"],
"inherit_when_omitted": true,
"must_equal_fields": ["screen_resolution", "motion"],
"identifier_merge": "union_by_type_and_value"
}
},
"spec": "docs/media-buy/product-discovery/media-products.mdx#dooh-placement-attributes"
},
"additionalProperties": true
}
},
"required": ["kind", "placement_id", "mode"],
"allOf": [
Expand Down
69 changes: 69 additions & 0 deletions static/schemas/source/core/placement-definition.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,75 @@
"uniqueItems": true,
"minItems": 1
},
"identifiers": {
"type": "array",
"description": "Optional external inventory identifiers for this placement, using the same {type, value} shape as property identifiers. Externally governed IDs should be authority-prefixed (e.g., space:1234931339, geopath:30961, fcc:73953). Seller-local IDs are opaque values scoped by the surrounding publisher namespace. Product-level placement declarations may carry additional identifiers but SHOULD NOT contradict publisher-declared identifiers for the same type.",
"items": {
"type": "object",
"properties": {
"type": {
"$ref": "/schemas/enums/identifier-types.json"
},
"value": {
"type": "string",
"description": "Identifier value, optionally authority-prefixed for externally governed IDs (e.g., space:1234931339)."
}
},
"required": ["type", "value"],
"additionalProperties": true
},
"uniqueItems": true,
"minItems": 1
},
"dooh_placement_attributes": {
"type": "object",
"description": "Publisher-declared DOOH inventory facts for digital out-of-home placements. These fields describe the screen and default scheduled loop; they do not define creative acceptance, which is governed exclusively by effective canonical format_options. Each field is optional and should only be populated when it is true for the placement being represented. A single screen/frame placement can include all fields; a package/network placement should include fields only when they are uniform across the included inventory. Referencing products may override slot_duration_seconds and loop_duration_seconds for a specific offer. screen_resolution and motion are intrinsic publisher facts and MUST NOT be changed by a referencing product.",
"properties": {
"slot_duration_seconds": {
"type": "integer",
"description": "Default scheduled duration of one ad slot in seconds (e.g., 10, 15, 30). This is an inventory fact used for loop and share calculations, not the creative-duration contract; format_options remains authoritative for accepted creative durations.",
"minimum": 1
},
"loop_duration_seconds": {
"type": "integer",
"description": "Duration of the full ad loop rotation in seconds. Buyers can derive nominal slot share as slot_duration_seconds / loop_duration_seconds. This is the canonical source for loop duration; the pricing-layer field in flat-rate-option.json is superseded by this placement-level declaration.",
"minimum": 1
},
"screen_resolution": {
"type": "object",
"description": "Physical screen resolution in pixels. Buyers can derive aspect ratio from width/height. This does not replace canonical format dimensions, which remain authoritative for creative acceptance and may describe a smaller content region within the physical screen.",
"properties": {
"width": {
"type": "integer",
"description": "Screen width in pixels.",
"minimum": 1
},
"height": {
"type": "integer",
"description": "Screen height in pixels.",
"minimum": 1
}
},
"required": ["width", "height"],
"additionalProperties": false
},
"motion": {
"$ref": "/schemas/enums/dooh-motion-type.json",
"description": "Physical motion capability of the DOOH screen. This is discovery metadata, not an accepted-format declaration; effective format_options determines whether a particular full-motion, partial-motion, or static creative is accepted."
}
},
"x-adcp-validation": {
"verifier_constraints": {
"slot_fits_loop": {
"left_path": "slot_duration_seconds",
"operator": "less_than_or_equal",
"right_path": "loop_duration_seconds"
}
},
"spec": "docs/media-buy/product-discovery/media-products.mdx#dooh-placement-attributes"
},
"additionalProperties": true
},
"ext": {
"$ref": "/schemas/core/ext.json"
}
Expand Down
76 changes: 76 additions & 0 deletions static/schemas/source/core/placement.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,82 @@
},
"uniqueItems": true,
"minItems": 1
},
"identifiers": {
"type": "array",
"description": "Optional external inventory identifiers for this placement, using the same {type, value} shape as property identifiers. Externally governed IDs should be authority-prefixed (e.g., space:1234931339, geopath:30961, fcc:73953). Seller-local IDs are opaque values scoped by the surrounding publisher namespace. Useful for DOOH screen/venue IDs, broadcast facility IDs, and any channel where placements map to externally registered inventory. For kind: publisher_ref, the effective identifier set is the union of the resolved publisher declaration and this product declaration, de-duplicated by exact (type, value); a product cannot suppress a publisher-declared identifier by omission.",
"items": {
"type": "object",
"properties": {
"type": {
"$ref": "/schemas/enums/identifier-types.json"
},
"value": {
"type": "string",
"description": "Identifier value, optionally authority-prefixed for externally governed IDs (e.g., space:1234931339)."
}
},
"required": ["type", "value"],
"additionalProperties": true
},
"uniqueItems": true,
"minItems": 1
},
"dooh_placement_attributes": {
"type": "object",
"description": "DOOH-specific inventory facts for digital out-of-home placements. These fields describe the screen and scheduled loop; they do not define creative acceptance, which is governed exclusively by the placement's effective canonical format_options. Each field is optional and should only be populated when it is true for the placement being represented. A single screen/frame placement can include all fields; a package/network placement should include fields only when they are uniform across the included inventory. For kind: publisher_ref, resolve the publisher placement first. Product-level slot_duration_seconds and loop_duration_seconds override publisher defaults for this offer; omitted values inherit. screen_resolution and motion are intrinsic publisher facts: when repeated at product level they MUST equal the publisher values. A mismatch is a conformance error rather than an override.",
"properties": {
"slot_duration_seconds": {
"type": "integer",
"description": "Scheduled duration of one ad slot in seconds (e.g., 10, 15, 30). This is an inventory fact used for loop and share calculations, not the creative-duration contract; format_options remains authoritative for accepted creative durations.",
"minimum": 1
},
"loop_duration_seconds": {
"type": "integer",
"description": "Duration of the full ad loop rotation in seconds. Buyers can derive nominal slot share as slot_duration_seconds / loop_duration_seconds. This is the canonical source for loop duration; the pricing-layer field in flat-rate-option.json is superseded by this placement-level declaration.",
"minimum": 1
},
"screen_resolution": {
"type": "object",
"description": "Physical screen resolution in pixels. Buyers can derive aspect ratio from width/height. This does not replace or broaden canonical format dimensions, which remain authoritative for creative acceptance and may describe a smaller content region within the physical screen.",
"properties": {
"width": {
"type": "integer",
"description": "Screen width in pixels.",
"minimum": 1
},
"height": {
"type": "integer",
"description": "Screen height in pixels.",
"minimum": 1
}
},
"required": ["width", "height"],
"additionalProperties": false
},
"motion": {
"$ref": "/schemas/enums/dooh-motion-type.json",
"description": "Physical motion capability of the DOOH screen. This is discovery metadata, not an accepted-format declaration; effective format_options determines whether a particular full-motion, partial-motion, or static creative is accepted."
}
},
"x-adcp-validation": {
"verifier_constraints": {
"slot_fits_loop": {
"left_path": "slot_duration_seconds",
"operator": "less_than_or_equal",
"right_path": "loop_duration_seconds",
"evaluate_after": "publisher_ref_resolution"
},
"publisher_ref_resolution": {
"override_fields": ["slot_duration_seconds", "loop_duration_seconds"],
"inherit_when_omitted": true,
"must_equal_fields": ["screen_resolution", "motion"],
"identifier_merge": "union_by_type_and_value"
}
},
"spec": "docs/media-buy/product-discovery/media-products.mdx#dooh-placement-attributes"
},
"additionalProperties": true
}
},
"required": ["kind", "placement_id", "mode"],
Expand Down
21 changes: 21 additions & 0 deletions static/schemas/source/enums/dooh-motion-type.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/enums/dooh-motion-type.json",
"title": "DOOH Motion Type",
"description": "Physical motion capability of a digital out-of-home screen. This is inventory discovery metadata; canonical format_options remains the authoritative declaration of which creatives the placement accepts.",
"type": "string",
"enum": [
"full_motion",
"partial_motion",
"static"
],
"enumDescriptions": {
"full_motion": "Screen supports full-motion video playback (e.g., LED billboards, transit screens with video capability)",
"partial_motion": "Screen supports animated stills or limited motion (e.g., scrolling text, animated GIFs, HTML5 banners) but not full video",
"static": "Screen displays static images only (e.g., e-ink displays, printed poster replacements)"
},
"examples": [
"full_motion",
"static"
]
}
Loading
Loading