Skip to content

[Change] § 5.6 — Give cost_method an extensible vocabulary, and say what several pricing[] entries mean #94

Open

Description

@KonstantinMirin

What kind of change?

Change a field's type or shape

Which section?

5.6 — Commercial terms

Current text

| `pricing[]` | Array[Object] | mixed | See below |
| `pricing[].cost_method` | Enum | seller-set | CPM, CPC, flat, etc. |

Proposed text

§ 5.6, replace the cost_method row with:

| `pricing[].pricing_id` | String | immutable | Names the entry. Never reused for another `cost_method`, `cost_parameters`, `currency`, or `transaction_mechanisms[]` |
| `pricing[].cost_method` | String | seller-set | The billable unit: a core term (Cost methods, below), or an extension term `<domain>:<name>` |
| `pricing[].rate_per` | Number | seller-set | REQUIRED for an extension term. Units per rate: `1` or `1000` |
| `pricing[].unit` | String | seller-set | REQUIRED for an extension term. A short label for one unit, such as `engagement` |
| `pricing[].cost_definition` | String (URL) | seller-set | REQUIRED for an extension term. Public definition of one unit and the `cost_parameters` keys, on the term's prefix domain or a subdomain |
| `pricing[].cost_parameters` | Object | seller-set | The keys the term requires |
| `pricing[].transaction_mechanisms[]` | Array[Enum] | seller-set | OPTIONAL. Mechanisms this entry prices. Absent: all offered |

After the table:

`pricing[]` entries are alternatives. The buyer commits under exactly
one, named by `commitment.pricing_id`; it MUST cover the selected
mechanism. Only that entry enters `agreed_rate`, `derived_units`, and the
`floor` comparison. Two entries MUST NOT share `cost_method`,
`cost_parameters`, `currency`, and a mechanism.

A line item MAY offer entries in several currencies. A commitment is in
exactly one: the committed entry's `currency`. `commitment.currency`, when
present, MUST equal it, and `min_spend`, the `commitment_bounds` budgets,
and § 3.1 `delta_flat` are read in it. OpenProposal never converts.

§ 5.6.1, add: `pricing_id`, REQUIRED when `pricing[]` has more than one entry.

After § 5.6.2, add an unnumbered subsection:

#### Cost methods

An IAB-maintained vocabulary, versioned apart from the schema; adding a
term needs no schema change. Bare names belong to it and compare
case-insensitively, so `flat` is `FLAT`:

| `cost_method` | One unit | Rate per | `cost_parameters` |
| :--- | :--- | :--- | :--- |
| `CPM` | impression | 1,000 | |
| `CPMV` | viewable impression | 1,000 | `viewability_standard` |
| `CPC` | click | 1 | |
| `CPCV` | completed view | 1 | |
| `CPV` | view | 1 | `view_threshold` |
| `CPP` | rating point | 1 | `demographic`, `rating_source` |
| `CPA` | action | 1 | `event`, `attribution_window`, `event_source` |
| `PLAY` | DOOH play on one screen or one sync group | 1,000 | |
| `TIME` | `time_unit` | 1 | `time_unit`: `hour` \| `day` \| `week` |
| `FLAT` | line item | | |

Anyone MAY define an extension term. Its prefix is a domain the definer
controls, such as `example-ssp.com:cpa_guaranteed`; a seller's own terms
MUST use its `seller_domain`. The `cost_definition` URL's host MUST be
the prefix domain or a subdomain of it. A term's meaning MUST NOT change once
published; a changed meaning takes a new name.

When `basis` is `spend`, `amount` is the seller total, on the same
terms as `agreed_rate`, including selected `delta_flat` charges
(§ 3.1), which MUST NOT exceed it. `FLAT` commits on `spend`.
For other entries, `derived_units` = (`amount` − those charges) ÷
`agreed_rate` × rate per, rounded down to a whole unit; `TIME` units are `time_unit`s from
`flight_start` through `flight_end`, inclusive. When `basis` is `units`, the seller total is `amount` ÷ rate
per × `agreed_rate` plus those charges; `min_spend` and
`commitment_bounds` budgets apply to it. A seller fee is never a
`pricing[]` entry. A consumer MUST treat an entry with an unrecognized
`cost_method`, or an extension entry that breaks these rules, as
unavailable, not the line item as invalid.

§ 3.1: `delta_cpm` adjusts only `CPM` and `CPMV` entries; an option
priced only by `delta_cpm` MUST NOT resolve against any other entry.

Why

We ask two separable things: a vocabulary for cost_method, and a meaning for several pricing[] entries. Either can be adopted alone.

cost_method is the billable unit, with no list and no meaning. § 5.6 ends the Enum with "etc.", and § 5.6.1 derives units from it by an unstated divisor. Views, rating points, and actions also need counting parameters.

A closed core list alone fails: a new model, such as the outcome-based pricing #80 asks for, waits for registration, and a proprietary one never gets in. Sellers write flat plus prose instead, as #45 does. Free strings fail the other way: CPCV and cost_per_completed_view never match.

A domain prefix can't collide, and it names who answers for the term, as <seller_namespace> does in § 2.2. With cost_definition on that domain, a reader checks a term's definer as it checks a seller (#61). Inline rate_per and unit keep the record self-contained. CPM, CPMV, CPC, daily TIME, and FLAT map to OpenDirect 2.1 ratetype; Ad Manager cost types add CPCV.

Nothing says what two pricing[] entries mean: a choice, a per-currency copy, or charges that add up. The comment template names the gap: "pricing[] sits above transaction_mechanism." A § 3.1 delta on transaction_mechanism changes only the rate, not the floor or unit. We ask: alternatives with an id, chosen once in commitment, which takes that entry's currency.

A spend amount includes selected delta_flat fees, as @aleksUIX's fee-basis PR (#117) proposes, and derived_units counts only the rest. Otherwise a fee overruns amount or goes unpaid.

Details: Pricing models and price alternatives: full rationale and examples

Knock-on effects

  • § 2.1: the summary lists each entry's pricing_id, cost_method, gross_rate, and currency.
  • Our reply on #30 locates an entry by cost_method; pricing_id replaces that. #63's record carries commitment.pricing_id.
  • commitment_bounds impressions count the committed entry's units; min_spend and the commitment_bounds budgets apply to the seller total, selected delta_flat charges included.
  • @dineshbhat's commitment.amount change request (#115) makes a seller fee its own pricing[] entry. Entries here are alternatives, so a fee there would be another choice, not a charge.
  • Whether agreed_rate is gross or net of agency commission stays open in our floor and rate-basis reply on #30, open question 3, and tax in the billing.tax_inclusive flag that #115 proposes. amount uses the same terms as agreed_rate, so the rule holds either way.
  • § 1.1 carriers take five OpenDirect rate types and deals take CPM; the controlling counter bills other terms.
  • #82 asks what a DOOH CPM counts; our DOOH plays filing answers it with impression_multiplier.
  • Breaking: commitment.pricing_id is REQUIRED with several entries; delta_cpm options, § 5.3 audiences included, resolve only against CPM and CPMV entries. flat stays valid.

Open questions for the working group

  1. Entries with different cost_methods share one commitment_bounds, so should min_impressions and max_impressions count the committed entry's units, as we propose, be renamed min_units and max_units, move into each entry, or give way to budget-only bounds?
  2. Should § 3.1 Selectable structure delta_cpm adjust only CPM and CPMV entries, as we propose, or read as an adjustment per rate unit on every entry?
  3. Should § 5.3 Audience reach audiences accept delta_pct and delta_flat, so that audience options can price non-CPM entries, or should audience options resolve only against CPM and CPMV entries?
  4. Should a programmatic guaranteed or private auction entry in a term that the § 1.1 Downstream standards deal carriers can't express, such as CPCV, be allowed and billed outside the deal carrier on the controlling count, as we propose, discouraged with a SHOULD NOT, or forbidden?
  5. Should the buyer name its entry in commitment.pricing_id, as we propose, or should pricing become a § 3.1 Selectable structure selectable field that the buyer sets through the selected[] key proposed in the buyer's selection key (#22)?

Would this break an implementation already built to this draft?

Yes — breaking

Alternatives you considered and rejected

  • A closed registry, as #62 uses for schemes. The closed list's delay; we keep its unknown-token rule.
  • AdCOM 1.0's "vendor-specific codes typically starting at 500". Two vendors can pick the same code.
  • Prefix core terms too (iab:CPM). Breaks every example.
  • One price per line item. Duplicate line items over one inventory; § 2 subsetting lets a buyer commit both.
  • Revenue share. A commission on attributed sales, not a rate on a unit (gist).

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-10-27: A PLAY unit is one play on a screen or sync group, following @premesh, and derived_units rounds down to a whole unit, following @EvgenyAndroid.

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. KonstantinMirin commented on Oct 25, 2026

    @KonstantinMirin
    Author

    Revision, from @aleksUIX's fee-basis PR (#117), on @dineshbhat's commitment.amount change request (#115). Our derived_units sentence reads all of a spend amount as media, so a selected delta_flat fee overruns amount or goes unpaid. We replace that whole sentence with:

    When `basis` is `spend`, `amount` is the seller total, on the same
    terms as `agreed_rate`, including selected `delta_flat` charges
    (§ 3.1), which MUST NOT exceed it. `FLAT` commits on `spend`.
    For other entries, `derived_units` = (`amount` − those charges) ÷
    `agreed_rate` × rate per; `TIME` units are `time_unit`s within the
    flight. When `basis` is `units`, the seller total is `amount` ÷ rate
    per × `agreed_rate` plus those charges; `min_spend` and
    `commitment_bounds` budgets apply to it. A seller fee is never a
    `pricing[]` entry.

    Gross or net of agency commission stays open in our floor and rate-basis reply on #30, open question 3, and tax in the billing.tax_inclusive flag that #115 proposes. amount uses the same terms as agreed_rate, so the formula holds either way.

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

  3. EvgenyAndroid commented on Oct 27, 2026

    @EvgenyAndroid

    Implementation evidence for the "entries are alternatives" rule and pricing_id. Without them I can only execute a line item with exactly one pricing[] entry. With two, nothing tells me whether they combine or which one the buyer accepted, and a selectable option's price delta can't say which entry it modifies.

    One gap this and #103 don't close yet: rounding. The display example gives derived_units: 3333333 for 80,000.00 at a 24.00 CPM, which truncates 3,333,333.33. "Informative, not binding" is fine, but two implementations should still get the same number. A stated rule (truncate, or round half-even) would make it reproducible.

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