Skip to content

[Change] § 5.6 — Give cancellation_policy and exclusivity options a typed shape #98

Open

Description

@KonstantinMirin

What kind of change?

Change a field's type or shape

Which section?

5.6 — Commercial terms

Current text

| `cancellation_policy` | Object | selectable | `available[]` of cancellation rights with `pricing` per option |
| `exclusivity` | Object | selectable | `available[]` of exclusivity terms with `pricing` |

Proposed text

§ 5.6, replace the two rows, and add § 5.6.6 and § 5.6.7:

| `cancellation_policy` | Selectable[CancellationOption] | OPTIONAL | selectable | See § 5.6.6. Absent: this field states no right to cancel, and `terms_refs[]` (§ 5.8) may state one |
| `exclusivity` | Selectable[ExclusivityOption] | OPTIONAL | selectable | See § 5.6.7. Nothing in effect: no exclusivity |

#### 5.6.6 Cancellation option

**`CancellationOption`** keys:

| Key | Type | Required | Meaning |
| :--- | :--- | :--- | :--- |
| `id` | String | REQUIRED | Option id (§ 3.1) |
| `notice_duration` | Duration | REQUIRED | Time from notice received to the earliest effective date: `P14D` is 14 days, `P0D` immediate |
| `max_share` | Fraction | REQUIRED | Cancellable share of the remaining commitment. `0`: non-cancellable |
| `fees[]` | Array[Fee] | REQUIRED | The fee, by how long before the reference date notice arrives. MUST be non-empty |
| `notice_reference` | VocabularyTerm | OPTIONAL | The date `before_reference` counts to. Core terms: `flight` (default) \| `install_date`. `flight`: `commitment.flight.start`. `install_date`: the posting or install date. No key in the record holds that date, so a fee that counts to it can't be computed from the record |
| `dated_shares[]` | Array[DatedShare] | OPTIONAL | Dates with their own `max_share`. Outside every entry's `window`, the top-level `max_share` applies |

**`Fee`** keys:

| Key | Type | Required | Meaning |
| :--- | :--- | :--- | :--- |
| `before_reference` | Duration | OPTIONAL | Default `P0D`. Measured from notice received to the `notice_reference` date, or to the `window.start` of the `dated_shares[]` entry that applies. The entry with the longest `before_reference` the notice meets applies; notice that meets none takes the shortest |
| `calculation` | Enum | REQUIRED | `none` \| `cancelled` \| `commitment` \| `fixed`. `none`: no fee. `cancelled`: `share` of the cancelled amount. `commitment`: `share` of `commitment.amount` without `delta_flat` charges, whatever has been delivered. `fixed`: `amount` |
| `share` | Fraction | REQUIRED when `calculation` is `cancelled` or `commitment` | The fee as a share |
| `amount` | Amount | REQUIRED when `calculation` is `fixed` | The fee as money |

**`DatedShare`** keys. An entry applies when the effective date falls in its `window`.
Notice then counts to `window.start`.

| Key | Type | Required | Meaning |
| :--- | :--- | :--- | :--- |
| `window` | Interval | REQUIRED | The dates it covers. An option's windows don't overlap |
| `max_share` | Fraction | REQUIRED | Cancellable share of the remaining commitment when this entry applies |

The effective date is the later of the date the buyer asks for and notice received
plus `notice_duration`. The remaining commitment is `commitment.amount`, without
`delta_flat` charges, less delivery (§ 5.6.5) through the effective date; the
cancelled amount is the part of it the buyer cancels, up to the `max_share` that
applies. These, and the base of a `commitment` fee, are money (§ 5.6): units and days
are valued as § 5.6.5 values delivery. The buyer owes that delivery, `production`
(§ 5.4) work completed by then, and the fee.

Exercise happens downstream (§ 1.1), against the option in effect. An entry's
`cancellation` (§ 4.4) records the notice. It changes no term, and the line item's
`status` stays `agreed`.

**`CancellationNotice`** keys:

| Key | Type | Required | Meaning |
| :--- | :--- | :--- | :--- |
| `option_id` | String | REQUIRED | The `id` of the option in effect that the notice exercises |
| `notice_received_at` | Instant | REQUIRED | When the seller received notice |
| `requested_effective_at` | Instant | REQUIRED | The effective date the buyer asks for |
| `cancelled_share` | Fraction | REQUIRED | The share of the remaining commitment cancelled, up to the `max_share` that applies |

#### 5.6.7 Exclusivity option

**`ExclusivityOption`** keys:

| Key | Type | Required | Meaning |
| :--- | :--- | :--- | :--- |
| `id` | String | REQUIRED | Option id (§ 3.1) |
| `scope` | Enum | REQUIRED | `category` \| `sole`. `category`: no other advertiser in `categories`. `sole`: no other advertiser |
| `categories` | TaxonomyCodes | REQUIRED when `scope` is `category` | `taxonomy` per AdCOM 1.0 List: Category Taxonomies. An id covers its descendants |
| `competitors[]` | Array[RegistrableDomain] | OPTIONAL | Advertiser domains also excluded |
| `separation` | VocabularyTerm | REQUIRED | Where the term holds. Core terms: `inventory` (all of this line item) \| `page` (each page) \| `pod` (each ad pod) \| `stream` (each content stream an ad server fills) \| `episode` (each podcast episode) \| `loop` (each DOOH screen's loop) |
| `window` | Interval | OPTIONAL | The dates the term holds, only where they fall inside `commitment.flight`. Default: the whole flight. With no overlap, the term holds on no date and the option's deltas still count (§ 5.6) |

A seller MUST decline a `commitment` whose term in effect conflicts with an
accepted term on the same inventory and dates.

Why

§ 5.6 prices cancellation and exclusivity options but never defines an option. A buyer agent pays a delta for an id it can't evaluate. We keep the priced tiers and give each option a type.

The cancellation keys follow practice. The IAB/4A's Standard Terms and Conditions 3.0, § V(a), sets notice by line type: 14 days guaranteed, 7 non-guaranteed, 30 flat-fee or fixed placement. § V(a)(iv) keeps custom material owed. TV upfront options cancel a share per quarter, which dated_shares[] carries.

@clerongi, a seller, asks in #85, time-based cancellation penalties for OOH and DOOH fees that step at 60, 30, or 7 days before the start. fees[] takes that schedule. In OOH, notice often counts to the posting or install date, "because printed material and labor are already committed", so notice_reference follows @premesh's comment. Static OOH (#125) asks for the keys that would carry that date.

For exclusivity, Google Ad Manager competitive exclusion separates by page, pod, or stream. OpenRTB 2.6 cattax names the taxonomy, a value from AdCOM 1.0 List: Category Taxonomies. Separation units differ by channel, so separation is a VocabularyTerm, as cost_method is in our cost-method change request (#94).

For dominations, @premesh (OUTFRONT Media) asks on #93 for loop and location. loop is a core term, read against the screen loop in our DOOH properties change request (#96). location covers units outside the line item, and #96 gives a property a venue type, not a venue. So location waits on open question 6.

A cancellation applies downstream, as our agreed-baseline comment (#68) asks. Our pricing PR carries this text.

As drafted (§ 5.6):

sequenceDiagram
    participant B as Buyer agent
    participant S as Seller
    participant D as Downstream system
    B->>S: cancel under option "notice-14d", effective 2026-12-07
    S->>S: read agreed record: an id and a price delta
    Note over S: effective date, share, fee: no defined keys
    Note over S,D: nothing defined to apply downstream
Loading

Proposed:

sequenceDiagram
    participant B as Buyer agent
    participant S as Seller
    participant D as Downstream system
    B->>S: change request: cancellation under "notice-14d", effective 2026-12-07
    S->>S: effective date = later of requested date and notice + P14D
    S->>S: max_share and the fees[] entry read from the option
    S->>D: apply: flight end, fee
    Note over B,S: change_requests[] records the notice; agreed terms stay frozen
Loading

Details and examples: Cancellation and exclusivity terms: full rationale and examples

Knock-on effects

  • Breaking: an option with ad-hoc keys, or without the REQUIRED keys, no longer conforms.
  • § 6: the CTV line item offers three notice_duration values. The audio line item has a stepped fee and priced exclusivity, and the issued proposal records a notice.
  • § 4.4, which our post-agreement PR adds: each change_requests[] entry can carry cancellation, a CancellationNotice. § 5.6.5 Cost methods comes with our pricing PR.
  • The conflict rule uses the seller decline in our lifecycle PR, which lists an exclusivity conflict among the allowed grounds. Separating competitors who bought without a term stays with the ad server.

Open questions for the working group

  1. Should the summary representation in § 2.1 Summary and full representations show whether a line item is cancellable? It could show the included option's notice_duration, or both fields could stay out of the summary.
  2. When notice arrives too late for the requested date, should the effective date move? IAB/4A's Standard Terms and Conditions 3.0, § V(a) moves it, and so does this change. Or should a seller be able to charge a short-notice fee instead?
  3. Should the cancellation option carry a short_rate key for the short rates in IAB/4A's Standard Terms and Conditions 3.0, § V(c), and a force majeure flag? Or should both stay in the legal terms that terms_refs[] points to, per relationship and contract references (#27)?
  4. Answered in our shared types PR. Should these category ids and the prefixed strings in buyer-supplied block lists (#25), such as "IAB-CT3.0:<id>", share one form with audience taxonomy ids? Our change request on audience disclosure (#97) asks for that. Category ids here and in block lists share TaxonomyCodes { taxonomy, ids[] }. In our audience PR, an audience option also names its taxonomy with an integer, from the OpenRTB segment taxonomies list. Its data_label carries the ids.
  5. Answered in part in our parties PR: To separate competitors who bought without a term at serve time, should the buyer declare its advertiser domain and category? These are adomain and cat in OpenRTB 2.6 Object: Bid. They could go in assets[] under § 5.4 Creative requirements, or stay with the creative downstream. @EvgenyAndroid's advertiser identity feedback (#126) noted that no field named the advertiser a commitment is for. commitment.advertisers[] names it by domain, which compares with competitors[]. Still open: the advertiser's category, and whether the seller reads either at serve time.
  6. Answered in part by @premesh on this change request: Should one exclusivity term, scoped by location, live at the proposal or package level? Line items would reference it, as @premesh suggests on #93. A domination sold as several line items would then share one term, so their terms can't drift apart in negotiation. That term would name the venue that location separation needs. Open question 3 on static OOH (#125) asks how static and digital line items reference each other. It has to fit the no-inheritance rule (§ 2), the proposal's binding terms (OQ-5 — Is a min_line_items field needed? (#6)), and line-item dependencies (#23). From the seller side, yes. location "needs a scope that can sit across line items", so "the term lives at proposal or package level, with line items referencing its id". Their comment on #6 adds that station dominations are bundles bought whole, and backs [Change] § 2, § 4.1, § 5.6 — Line-item dependencies and bonus lines (linked_execution, requires) #23's linked_execution: all for them. Still open: how the term fits the no-inheritance rule, and how it names a venue.
  7. Should the text state the cancellation and exclusivity rules we left out? The rules issue lists them.

Would this break an implementation already built to this draft?

Yes — breaking

Alternatives you considered and rejected

A fixed exclusivity attribute loses the tiers. A terms_refs[] document (#27) isn't machine-evaluable.

You are commenting as

Other — IAB Tech Lab member (Postindustria).

Before you submit

  • I have read CONTRIBUTING.md and agree my contribution may be published under this repository's licence.
  • This contains no confidential or commercially sensitive information.

Revised 2026-11-03: Rewritten to the names and types in our pricing PR, with the notice recorded in change_requests[]. Question 4 is answered and question 5 in part; question 7 is new.

Analyzed and generated with the help of Claude, fully reviewed by me.

Activity

  1. added
    change-requestProposes specific normative wording
    normativeChanges what the spec requires
    breakingWould break an implementation already built to this draft
    on Oct 23, 2026
  2. premesh commented on Oct 27, 2026

    @premesh

    The separation values are all web, CTV and audio constructs. For OOH, an exclusivity term needs two more:

    • loop: no competitor in the same digital loop on the covered screens. This is what a DOOH category exclusive usually means.
    • location: no competitor on any face at the venue or site, static or digital. This is what a station or venue domination sells.

    location needs a scope that can sit across line items, since a domination is usually an ooh and a dooh line item together (see my reply on #93 OQ7). That suggests the term lives at proposal or package level, with line items referencing its id.

    On notice: OOH cancellation is commonly measured against the posting or install date, not just the flight start, because printed material and labor are already committed. A notice_reference: flight_start | install_date key would cover it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingWould break an implementation already built to this draftchange-requestProposes specific normative wordingnormativeChanges what the spec requiressection:5.6-commercial

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions