Skip to content
Draft
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
1 change: 1 addition & 0 deletions examples/line-item-ctv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ status: published
channel: ctv
properties: { available: [{ id: "weather-app" }], included: ["weather-app"] }
environments: { available: ["ctv"], included: ["ctv"] }
placement: { plcmt: 1 } # Instream (1) (§ 5.2)
formats: { available: ["ctv/instream-15s", "ctv/instream-30s"],
included: ["ctv/instream-15s"],
pricing: { "ctv/instream-30s": { delta_cpm: 8.00 } } }
Expand Down
37 changes: 37 additions & 0 deletions examples/line-item-rewarded-video.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# OpenProposal 3.0 — Example: Rewarded video line item (spec § 6.5)
# Illustrative only. In-app rewarded video across a pack of puzzle games.
line_item_id: "puzzle-games-rewarded"
status: published
channel: video
properties: { available: [{ id: "puzzle-pack", name: "Puzzle game pack" }],
included: ["puzzle-pack"] }
environments: { available: ["app_mobile"], included: ["app_mobile"] }
placement: # every impression of this line item (§ 5.2)
reward: 1
instl: 1
plcmt: 3 # Interstitial (3)
skip: 0 # watched to the end for the reward
playmethod: [3] # Initiates on Click with Sound On (3)
api: [6, 7] # MRAID 3.0 (6), OMID 1.0 (7)
formats: { available: ["video/30s"], included: ["video/30s"] }
creative_delivery: { method: buyer_hosted, protocol: vast_4_2, secure_required: true }

frequency_cap: { max: { min: 1, max: 3 }, period: "day", basis: device }

pricing:
- { cost_method: CPM, gross_rate: 22.00, floor: 19.50,
price_valid_until: "2026-12-01T23:59:59Z", currency: USD }

commitment_bounds: { min_budget: 10000.00 }

committed_metrics:
- { metric: completion_rate, value: 0.95, basis: mrc }
- { metric: viewable_rate, value: 0.90, basis: mrc }
measurement_source: # third-party vendors run through OMID 1.0 (7)
available: [{ vendor: "IAS", attribution_window: "30d" },
{ vendor: "DoubleVerify", attribution_window: "30d" }]
included: ["IAS"]

buying_route: SSP_PLATFORM
transaction_mechanism: { available: ["PREFERRED_DEAL"], included: ["PREFERRED_DEAL"] }
serving_mode: client_side
6 changes: 6 additions & 0 deletions spec/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ issue that caused it, so the comment record and the spec history line up.

## [Unreleased]

- § 2.1, § 5.2 — `placement` on the line item states what every impression
is, in AdCOM 1.0 and OpenRTB 2.6 terms: rewarded, interstitial, video
placement subtype, skippability, playback, API frameworks and refresh.
`plcmt` is REQUIRED for video ads. Breaking
([#89](https://github.com/IABTechLab/OpenProposal/issues/89)).

## [3.0-draft-1] — 2026-09-22

Initial draft published for public comment. Ported from the working-group
Expand Down
66 changes: 63 additions & 3 deletions spec/openproposal-3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ plus, from the parent proposal: `proposal_id` · `seller_name` · `description`

`catalog_ref` values MUST remain unresolved in a summary.

A line item that carries `placement` (§ 5.2) MUST also give, in its summary,
whichever of `placement.reward` · `placement.instl` · `placement.plcmt` ·
`placement.skip` it declares.

**Full Representation** — every field, with all `catalog_ref` values resolved.
A full fetch MUST return it.

Expand Down Expand Up @@ -225,6 +229,55 @@ channel.
| `properties` | Object | selectable | `available[]` of `{ id, name, domains, supported_formats }`, `included[]`, `pricing`, `max_select` |
| `content_detail` | Object | seller-set | Shows, series, sections, network verticals |
| `environments` | Object | selectable | `available[]` from `web_desktop` \| `web_mobile` \| `app_mobile` \| `ctv` \| `dooh` \| `audio_streaming` |
| `placement` | Object | seller-set | What every impression of this line item is: rewarded, interstitial, video placement subtype, skippability, playback, API frameworks and refresh. `plcmt` is REQUIRED for video ads. See 5.2.1 |

#### 5.2.1 placement

Keys follow [AdCOM 1.0 `Placement`](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/26c59ba1b235cd8d938c9877409e506f1b8d3f0c/AdCOM%20v1.0%20FINAL.md#object_placement) and its
[`DisplayPlacement`](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/26c59ba1b235cd8d938c9877409e506f1b8d3f0c/AdCOM%20v1.0%20FINAL.md#object_displayplacement),
[`VideoPlacement`](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/26c59ba1b235cd8d938c9877409e506f1b8d3f0c/AdCOM%20v1.0%20FINAL.md#object_videoplacement) and
[`AudioPlacement`](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/26c59ba1b235cd8d938c9877409e506f1b8d3f0c/AdCOM%20v1.0%20FINAL.md#object_audioplacement) subtypes, and the matching
[OpenRTB 2.6 `Imp`](https://github.com/InteractiveAdvertisingBureau/openrtb2.x/blob/1debba8001f40eb14597102dec3205afddf811d4/2.6.md#objectimp) attributes.

Each key takes an integer, except `playmethod[]` and `api[]`, which take
arrays of integers, and `refresh`, an object whose `min_interval_s` is a
REQUIRED integer. `plcmt`, `playmethod[]` and `api[]` take any code in
[List: Plcmt Subtypes - Video](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/26c59ba1b235cd8d938c9877409e506f1b8d3f0c/AdCOM%20v1.0%20FINAL.md#list_plcmtsubtypesvideo),
[List: Playback Methods](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/26c59ba1b235cd8d938c9877409e506f1b8d3f0c/AdCOM%20v1.0%20FINAL.md#list_playbackmethods) and
[List: API Frameworks](https://github.com/InteractiveAdvertisingBureau/AdCOM/blob/26c59ba1b235cd8d938c9877409e506f1b8d3f0c/AdCOM%20v1.0%20FINAL.md#list_apiframeworks) respectively. This
section names each code by its AdCOM name followed by the code, such as
Instream (1).

| Key | Required | Meaning | AdCOM 1.0, OpenRTB 2.6 |
| :--- | :--- | :--- | :--- |
| `reward` | OPTIONAL | `0` (no) \| `1` (yes). The user receives a reward for viewing the ad | `Placement.reward`, `imp.rwdd` |
| `instl` | OPTIONAL | `0` (no) \| `1` (yes). The ad is interstitial or full screen | `DisplayPlacement.instl`, `imp.instl` |
| `plcmt` | REQUIRED for video ads (`channel` `video`, or video on `channel` `ctv`); otherwise OPTIONAL | One value from List: Plcmt Subtypes - Video: Instream (1), Accompanying Content (2), Interstitial (3), No Content/Standalone (4), Pause (5), Screensaver (6), Overlay (7), Squeezeback (8), In-scene (9) | `VideoPlacement.ptype`, `video.plcmt` |
| `skip` | OPTIONAL | Video or audio. `0` (no) \| `1` (yes). The placement lets the user skip the ad | `VideoPlacement.skip`, `AudioPlacement.skip`, `video.skip` |
| `skipafter` | OPTIONAL | When `skip` is `1`: seconds the ad plays before the placement enables skipping | `VideoPlacement.skipafter`, `AudioPlacement.skipafter`, `video.skipafter` |
| `playmethod[]` | OPTIONAL | Video or audio. Values from List: Playback Methods in use, such as Initiates without any specific user interaction with Sound On (1), Initiates without any specific user interaction Sound Off by Default (2), Initiates on Click with Sound On (3) and Continuous Playback (7) | `VideoPlacement.playmethod`, `AudioPlacement.playmethod`, `video.playbackmethod` |
| `api[]` | OPTIONAL | Values from List: API Frameworks that every impression supports, such as MRAID 3.0 (6), OMID 1.0 (7) and SIMID 1.1 (9) | `DisplayPlacement.api`, `VideoPlacement.api`, `AudioPlacement.api`, `banner.api`, `video.api`, `audio.api` |
| `refresh` | OPTIONAL | `{ min_interval_s }`: minimum seconds a creative renders before the slot refreshes | OpenRTB 2.6 only: `imp.refresh.refsettings[].minint` |

`plcmt` takes its OpenRTB 2.6 name. AdCOM 1.0 carries the same list as
`VideoPlacement.ptype`. OpenRTB 2.6 `Audio` has no `skip`, `skipafter` or
`playbackmethod`, so for audio those keys map to AdCOM 1.0 `AudioPlacement`
only.

- A key that is present holds for every impression of the line item. An absent
key does not constrain it: a line item that omits `instl` can deliver to
interstitial and non-interstitial impressions alike. AdCOM 1.0 reads an
absent `reward` or `instl` as `0`; here it means either value.
- The seller MUST NOT deliver the line item to an impression that does not
match a key that is present. An impression matches `api[]` when it supports
every listed framework, `playmethod[]` when its playback method is one of the
listed values, and `refresh` when the slot renders each creative for at least
`min_interval_s` seconds before it refreshes, or does not refresh. It matches
any other key when its value is equal.
- Unless `api[]` includes OMID 1.0 (7), a line item whose `committed_metrics[]`
includes `viewable_rate` and whose `environments.available[]` includes
`app_mobile` or `ctv` SHOULD NOT offer a `measurement_source` vendor other
than the seller.

### 5.3 Audience reach

Expand Down Expand Up @@ -375,6 +428,12 @@ See [`examples/line-item-ctv.yaml`](../examples/line-item-ctv.yaml).

See [`examples/line-item-audio.yaml`](../examples/line-item-audio.yaml).

### 6.5 Rewarded video line item

Shows `placement` on in-app rewarded video, with OMID support that lets a
buyer select a third-party `measurement_source`. See
[`examples/line-item-rewarded-video.yaml`](../examples/line-item-rewarded-video.yaml).

---

## 7. Why this split
Expand Down Expand Up @@ -454,6 +513,7 @@ body is normative.**
| `properties` | 5.2 Inventory composition | Object | selectable |
| `content_detail` | 5.2 Inventory composition | Object | seller-set |
| `environments` | 5.2 Inventory composition | Object | selectable |
| `placement` | 5.2 Inventory composition | Object | seller-set |
| `audiences` | 5.3 Audience reach | Object | selectable |
| `addressable_scale` | 5.3 Audience reach | Integer | derived |
| `activation_route` | 5.3 Audience reach | Object | selectable |
Expand Down Expand Up @@ -512,7 +572,7 @@ body is normative.**
| Marker | Count | Meaning | Fields |
| :--- | :--- | :--- | :--- |
| immutable | 6 | Set once at creation. Neither party may change it. | `proposal_id`, `seller_id`, `seller_name`, `type`, `line_item_id`, `channel` |
| seller-set | 29 | Seller sets and may revise while `draft` or `published`. | `valid_from` · `valid_until`, `description`, `specifications`, `best_for[]`, `not_suitable_for[]`, `seasonality_notes`, `content_detail`, `specs`, `third_party_tags`, `creative_delivery`, `approval`, `pricing[].cost_method`, `pricing[].gross_rate`, `pricing[].seller_rate`, `pricing[].price_valid_until`, `pricing[].currency`, `pricing[].floor`, `commitment_bounds`, `min_spend`, `creative_policy`, `visibility`, `required_credentials[]`, `eligible_bidders[]`, `terms_acceptance_required`, `buying_route`, `serving_mode`, `serving_platform`, `billing`, `supported_order_systems[]` |
| seller-set | 30 | Seller sets and may revise while `draft` or `published`. | `valid_from` · `valid_until`, `description`, `specifications`, `best_for[]`, `not_suitable_for[]`, `seasonality_notes`, `content_detail`, `placement`, `specs`, `third_party_tags`, `creative_delivery`, `approval`, `pricing[].cost_method`, `pricing[].gross_rate`, `pricing[].seller_rate`, `pricing[].price_valid_until`, `pricing[].currency`, `pricing[].floor`, `commitment_bounds`, `min_spend`, `creative_policy`, `visibility`, `required_credentials[]`, `eligible_bidders[]`, `terms_acceptance_required`, `buying_route`, `serving_mode`, `serving_platform`, `billing`, `supported_order_systems[]` |
| selectable | 12 | Buyer chooses from a declared set. Resolves without assent. | `properties`, `environments`, `audiences`, `activation_route`, `formats`, `production`, `cancellation_policy`, `exclusivity`, `measurement_source`, `shortfall_remedy`, `reporting`, `transaction_mechanism` |
| settable | 5 | Buyer sets a value within declared bounds. Resolves without assent. | `geo`, `device`, `daypart`, `frequency_cap`, `commitment` |
| supplied | 3 | Buyer provides data or an artifact. | `brief_ref`, `buyer_supplied`, `assets[]` |
Expand All @@ -522,8 +582,8 @@ body is normative.**

### What the distribution says

Of 71 fields, **23 are touched by the buyer** — 12 `selectable`, 5 `settable`, 3
`supplied` and 3 `requestable`. The remaining 48 are `seller-set`, `immutable`,
Of 72 fields, **23 are touched by the buyer** — 12 `selectable`, 5 `settable`, 3
`supplied` and 3 `requestable`. The remaining 49 are `seller-set`, `immutable`,
`derived` or `mixed`.

Only the 3 `requestable` fields require a round trip. Everything else the buyer
Expand Down