> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adcontextprotocol.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Pricing Models

> AdCP pricing models — CPM, CPCV, CPP, CPC, CPA, revenue share, time-based, and DOOH pricing. Publisher-declared, buyer-selected model with rate card support.

AdCP supports multiple pricing models to accommodate different advertising channels and business objectives. Publishers declare which pricing models they support, and buyers select from available options.

## Publisher-Declared, Buyer-Selected Model

### How It Works

1. **Publishers declare pricing options** in their products via `pricing_options` array (each with unique `pricing_option_id`)
2. **Buyers discover available options** through `get_products`
3. **Buyers select a specific option** when creating a media buy via `pricing_option_id`
4. **Delivery is measured** according to the declared `delivery_measurement` provider

### Key Benefits

* **Flexibility**: Publishers can offer multiple pricing models for the same inventory
* **Currency Support**: Publishers specify supported currencies; buyers must match
* **Market Standards**: Each channel (TV, video, display, performance) can use its natural pricing unit
* **Clear Expectations**: Both parties agree on pricing before campaign launch

## Measurement & Source of Truth

### Measurement Provider as Source of Truth

**The product declares the measurement provider, and the buyer accepts that provider as the source of truth for the buy.**

Publishers specify their measurement provider in the product:

```json theme={null}
{
  "product_id": "premium_video",
  "delivery_measurement": {
    "provider": "Google Ad Manager with IAS viewability verification",
    "notes": "MRC-accredited viewability measurement. 50% in-view for 1 second (display) or 2 seconds (video)."
  }
}
```

**Common Measurement Providers:**

* **Ad Servers**: Google Ad Manager, Freewheel, SpringServe
* **Attention Metrics**: Adelaide, Lumen, TVision
* **Third-Party Verification**: IAS, DoubleVerify, Scope3
* **TV/Audio Measurement**: Nielsen, Comscore, iSpot.tv, Triton Digital
* **DOOH**: Geopath, Vistar, Place Exchange

By accepting the product, buyers agree to use the declared measurement provider as the authoritative source for delivery metrics.

### Measurement Terms and Performance Standards

Beyond declaring a provider, sellers can publish structured terms on products — and buyers can negotiate them at buy creation. These are two separate concerns:

* **`measurement_terms`** — Who counts the billing metric and what happens when thresholds are breached (makegoods)
* **`performance_standards`** — What rate thresholds apply (viewability, IVT, completion, brand safety, attention)

```json theme={null}
{
  "product_id": "premium_guaranteed_video",
  "delivery_measurement": {
    "provider": "Google Ad Manager with DoubleVerify verification",
    "notes": "MRC-accredited viewability. DV IVT filtering enabled."
  },
  "measurement_terms": {
    "billing_measurement": {
      "vendor": { "domain": "admanager.google.com" },
      "max_variance_percent": 10
    },
    "makegood_policy": {
      "available_remedies": ["additional_delivery", "credit", "invoice_adjustment"]
    }
  },
  "performance_standards": [
    {
      "metric": "viewability",
      "threshold": 0.70,
      "standard": "mrc",
      "vendor": { "domain": "doubleverify.com" }
    },
    {
      "metric": "ivt",
      "threshold": 0.05,
      "vendor": { "domain": "doubleverify.com" }
    },
    {
      "metric": "completion_rate",
      "threshold": 0.80,
      "vendor": { "domain": "doubleverify.com" }
    }
  ]
}
```

**Measurement terms fields:**

* **`billing_measurement`** — Which vendor's count of the billing metric (determined by `pricing_model`) governs invoicing. `max_variance_percent` defines the threshold at which the non-billing party's count divergence triggers resolution.
* **`makegood_policy`** — Closed menu of remedies available when any performance standard or billing variance is breached. Three remedy types: `additional_delivery` (extend/add impressions, like-for-like), `credit` (toward future buys), `invoice_adjustment` (reduce current invoice). Seller proposes from this menu; buyer accepts or disputes.

**Performance standards fields:**

Each entry in the `performance_standards` array specifies:

* **`metric`** — What is measured: `viewability`, `ivt`, `completion_rate`, `brand_safety`, `attention_score`
* **`threshold`** — Rate as a decimal (0-1). Whether this is a floor or ceiling depends on the metric: viewability, completion\_rate, brand\_safety, attention\_score are floors (must exceed); ivt is a ceiling (must not exceed).
* **`standard`** — Required for viewability (`"mrc"` or `"groupm"`). Omit for other metrics.
* **`vendor`** — Who measures it, identified by domain. When agreed on a confirmed package, creatives MUST include tracker assets from specified vendors.
* **`metric_id`** — Optional. Which of the vendor's metrics carries the value judged against `threshold`, in the vendor's own vocabulary (the `metric_id` in its `measurement.metrics[]` catalog and in delivery's `vendor_metric_values[]`). Allowed only for `ivt`, `brand_safety`, and `attention_score`. `viewability` and `completion_rate` are judged against their delivery scalars (`viewability` under `standard`; a vendor-attested completion rate carries `completion_source: vendor_attested`).

Verification vendors usually report several metrics per category: general, sophisticated, and combined invalid traffic rates, or several brand-safety rates. `metric_id` says which one the standard means:

```json theme={null}
{
  "metric": "ivt",
  "threshold": 0.03,
  "vendor": { "domain": "verification-vendor.example" },
  "metric_id": "givt_rate"
}
```

When `metric_id` is present:

* **Evidence.** The standard is judged only against `vendor_metric_values[]` entries from the same vendor (equal `domain` and `brand_id`) with the same `metric_id`. Values for the vendor's other metrics are not evidence for it. If several rows match because their `qualifier`s differ, only the row whose qualifier matches the package's vendor-scope `committed_metrics` entry counts; if that does not leave exactly one row, the standard is not judged.
* **Scale.** `metric_id` MUST name a metric the vendor reports as a 0–1 fraction, on the same scale as `threshold`. A reported value outside 0–1 is not evidence and MUST NOT be converted (for example from a percent).
* **Direction.** `metric_id` MUST name a metric that runs the same way as `metric`: for `ivt`, the share of invalid traffic (judged as a ceiling); for `brand_safety`, the share of safe impressions; for `attention_score`, higher is better (both judged as floors). An inverted metric such as an unsafe-impression rate cannot carry a `brand_safety` standard.
* **Denominator.** The judged value is the vendor's reported value as-is. The denominators in the `performance-standard-metric` enum descriptions do not apply.
* **Reporting.** A package whose standard carries `metric_id` SHOULD also carry a matching vendor-scope `committed_metrics` entry, so `missing_metrics` audits the value.
* **Versions.** The field is new in 3.3. Senders MUST omit `metric_id` toward counterparties pinned below 3.3: canonical 3.2 shapes reject it, and legacy shapes ignore it, so a standard sent that way does not bind the metric.

When `metric_id` is absent on an `ivt`, `brand_safety`, or `attention_score` standard, nothing says which vendor metric carries it. Parties MUST NOT infer one from the vendor's catalog or metric naming, and treat the standard as not judged from `vendor_metric_values`.

**Negotiation flow:**

1. Seller publishes `measurement_terms` and `performance_standards` on the product (defaults)
2. Buyer proposes overrides on the package request at `create_media_buy`
3. Seller accepts (echoes on confirmed package), rejects (`TERMS_REJECTED`), or adjusts (returns modified terms)
4. If a threshold is breached, seller proposes a remedy from the agreed `makegood_policy`; buyer accepts or disputes

When the buyer omits these fields, the product's defaults apply.

### Cancellation Policy

Guaranteed products can declare a `cancellation_policy` with the minimum notice period required before cancellation takes effect:

```json theme={null}
{
  "product_id": "premium_guaranteed_video",
  "delivery_type": "guaranteed",
  "cancellation_policy": {
    "notice_period": { "interval": 30, "unit": "days" },
    "cancellation_fee": { "type": "percent_remaining", "rate": 0.5 }
  }
}
```

**Cancellation fee types:**

* **`percent_remaining`** — Percentage of remaining committed spend (requires `rate`, e.g., `0.5` for 50%)
* **`full_commitment`** — Buyer owes the full committed budget regardless of delivery
* **`fixed_fee`** — Flat monetary amount (requires `amount` in the buy's currency)
* **`none`** — No cancellation fee (cancellation with notice is free)

Unlike `measurement_terms`, cancellation policy is not a negotiation surface — the seller declares it and the buyer accepts it by creating a media buy against the product. A guaranteed buy canceled without sufficient notice incurs the declared cancellation fee.

### Best Practices

**For Publishers:**

* Clearly identify your measurement provider (ad server and any third-party verification)
* Explain your measurement methodology in plain language
* For DOOH, specify your audience measurement source (e.g., Geopath, venue sensors)

**For Buyers:**

* Review the measurement provider before committing budget
* Ensure the provider meets your campaign requirements
* Negotiate audit rights in contracts if needed

## Supported Pricing Models

### CPM (Cost Per Mille)

**Cost per 1,000 impressions** - Traditional display advertising pricing.

**Use Cases**: Display, native, banner advertising

**Example**:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/cpm-option.json",
  "pricing_option_id": "cpm_usd_guaranteed",
  "pricing_model": "cpm",
  "fixed_price": 12.50,
  "currency": "USD",
  "min_spend_per_package": 5000
}
```

**Billing**: Charged per 1,000 ad impressions served

***

### vCPM (Viewable Cost Per Mille)

**Cost per 1,000 viewable impressions** - Payment only for impressions meeting MRC viewability standard.

**Use Cases**: Display, native, video advertising with viewability guarantee

**Viewability Standard**: MRC (Media Rating Council) standard requires:

* **Display ads**: 50% of pixels in-view for at least 1 continuous second
* **Video ads**: 50% of pixels in-view for at least 2 continuous seconds

**Example**:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/vcpm-option.json",
  "pricing_option_id": "vcpm_usd_guaranteed",
  "pricing_model": "vcpm",
  "fixed_price": 18.50,
  "currency": "USD",
  "min_spend_per_package": 5000
}
```

**Billing**: Charged per 1,000 viewable impressions (impressions meeting MRC viewability threshold). Viewability is measured by the declared measurement provider.

**Measurement**: When available, publishers declare their viewability measurement provider in the optional `delivery_measurement` field. Common providers include IAS (Integral Ad Science), DoubleVerify, MOAT, and Google Active View.

***

### CPCV (Cost Per Completed View)

**Cost per 100% video/audio completion** - Payment only for fully completed views.

**Use Cases**: Video campaigns, audio ads, pre-roll video

**Example**:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/cpcv-option.json",
  "pricing_option_id": "cpcv_usd_guaranteed",
  "pricing_model": "cpcv",
  "fixed_price": 0.15,
  "currency": "USD"
}
```

**Billing**: Charged only when viewer completes 100% of the video/audio ad. Completion is measured by the declared measurement provider.

***

### CPV (Cost Per View)

**Cost per view at threshold** - Payment when viewer reaches publisher-defined threshold.

**Use Cases**: Video campaigns with shorter completion requirements

**Example**:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/cpv-option.json",
  "pricing_option_id": "cpv_usd_50pct",
  "pricing_model": "cpv",
  "fixed_price": 0.08,
  "currency": "USD",
  "parameters": {
    "view_threshold": 0.5
  }
}
```

**Billing**: Charged when viewer reaches threshold (e.g., 50% completion, 30 seconds)

**Parameters**:

* `view_threshold`: Decimal from 0.0 to 1.0 (e.g., 0.5 = 50% completion)

***

### CPP (Cost Per Point)

**Cost per Gross Rating Point** - Traditional TV/radio buying metric.

**Use Cases**: Connected TV, linear TV, radio, audio streaming

**Example**:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/cpp-option.json",
  "pricing_option_id": "cpp_usd_a18-49",
  "pricing_model": "cpp",
  "fixed_price": 250.00,
  "currency": "USD",
  "parameters": {
    "demographic": "A18-49",
    "min_points": 50
  },
  "min_spend_per_package": 12500
}
```

**Billing**: Charged per rating point delivered to target demographic

**Parameters**:

* `demographic`: Target demographic (e.g., "A18-49", "W25-54", "M35+")
* `min_points`: Minimum GRP commitment required

**Metrics Reported**:

* `grps`: Total Gross Rating Points delivered
* `reach`: Unique individuals reached
* `frequency`: Average frequency per individual

**Measurement Requirements**:

CPP pricing requires **certified demographic measurement**. Publishers should declare their measurement provider:

```json theme={null}
{
  "pricing_model": "cpp",
  "fixed_price": 250.00,
  "delivery_measurement": {
    "provider": "Nielsen DAR",
    "notes": "Panel-based demographic measurement for A18-49. GRP reports available weekly."
  }
}
```

**Common Measurement Providers for CPP**:

* **Nielsen DAR/TV**: Industry-standard TV measurement
* **Comscore**: Campaign Ratings for CTV
* **iSpot.tv**: Advanced TV analytics
* **Triton Digital**: Audio/streaming measurement

Buyers should verify the measurement provider meets their campaign requirements before accepting CPP deals.

***

### CPC (Cost Per Click)

**Cost per click** - Performance-based pricing for engagement.

**Use Cases**: Direct response campaigns, search ads, social advertising

**Example**:

```json theme={null}
{
  "pricing_model": "cpc",
  "currency": "USD",
  "floor_price": 0.50,
  "price_guidance": {
    "p50": 1.20,
    "p75": 2.00
  }
}
```

**Billing**: Charged only when user clicks the ad

***

### CPA (Cost Per Acquisition)

**Cost per conversion event** - Advertiser pays when a defined conversion occurs.

**Use Cases**: Retail media (pay per order), lead generation, app install campaigns, commerce media

**Example**:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/cpa-option.json",
  "pricing_option_id": "cpa_usd_purchase",
  "pricing_model": "cpa",
  "event_type": "purchase",
  "fixed_price": 5.00,
  "currency": "USD"
}
```

**Billing**: Charged a fixed price when the specified `event_type` fires. The pricing option declares what event triggers billing — this is independent of `optimization_goals`, which controls delivery optimization.

**Parameters**:

* `event_type` (required): The conversion event that triggers billing. Uses the standard event type enum (e.g., `purchase`, `lead`, `app_install`, `add_to_cart`, `subscribe` for paid subscriptions, `follow` for free durable opt-ins).
* `event_source_id` (optional): When present, only events from this specific source count toward billing. Must match an event source configured via `sync_event_sources`. When omitted, any event of the specified `event_type` counts.

**Example** (different rates by event source):

```json theme={null}
{
  "pricing_options": [
    {
      "pricing_option_id": "cpa_online_purchase",
      "pricing_model": "cpa",
      "event_type": "purchase",
      "event_source_id": "website_pixel",
      "fixed_price": 5.00,
      "currency": "USD"
    },
    {
      "pricing_option_id": "cpa_instore_purchase",
      "pricing_model": "cpa",
      "event_type": "purchase",
      "event_source_id": "instore_attribution",
      "fixed_price": 3.00,
      "currency": "USD"
    }
  ]
}
```

**Pricing vs. optimization**: The CPA pricing option's `event_type` (what triggers billing) is independent of the package's `optimization_goals` (what the platform optimizes delivery toward). For example, a package can use CPA pricing on `lead` events while setting an event goal with `event_sources` containing purchase events and `target: { kind: "per_ad_spend", value: 4.0 }` — billing fires on leads, but delivery is optimized for downstream purchase return.

**Refunds and adjustments**: Refund handling and conversion adjustment policies are commercial terms between buyer and seller. The protocol does not govern clawbacks or billing credits for refunded conversions.

**Note**: CPA replaces the need for separate "CPO" (Cost Per Order) or "CPL" (Cost Per Lead) pricing models. A seller can offer multiple CPA options with different event types, event sources, and prices on the same product.

***

### Revenue Share

**Percentage of settled attributed value** - Advertiser pays a fixed commission rate on the portion of attributed conversion value eligible under the parties' agreement.

**Use Cases**: Affiliate networks, content commerce, performance partnerships, and other outcome-priced media

```json theme={null}
{
  "pricing_option_id": "affiliate_purchase_4pct",
  "pricing_model": "revenue_share",
  "event_type": "purchase",
  "event_source_id": "affiliate_attribution",
  "commission_rate": 0.04,
  "currency": "USD",
  "commission_basis_description": "Net merchandise value after discounts, excluding tax and shipping; returns removed before commission lock."
}
```

`commission_rate` is a decimal proportion: `0.04` means 4%. Revenue share uses contingent pricing, not fixed-unit or auction pricing, so the option does not carry `fixed_price`, `floor_price`, `price_guidance`, or `max_bid`, and the package request does not carry `bid_price`.

**Billing formula**:

```text theme={null}
spend = round_currency(commissionable_value * commission_rate)
```

`commissionable_value` is the settled value eligible for commission. It is distinct from `conversion_value`, which remains the total attributed value for analytics. The difference may reflect excluded tax or shipping, discounts, ineligible items, returns, or cancellations. `commission_basis_description` tells operators how the authoritative party calculates the eligible amount.

**Settlement flow**:

1. The buyer registers the declared `event_source_id` through `sync_event_sources`.
2. The package's `measurement_terms.billing_measurement` names the authoritative party and commission-lock `measurement_window`.
3. Provisional delivery may report conversion and commissionable values while the return window is open.
4. `adjusted` or `window_update` reports replace earlier values as returns and cancellations settle.
5. A final row reports `commissionable_value`, calculated `spend`, `is_final: true`, and `finalized_at`.

For revenue share, package `budget` is the maximum commission payable—not attributed sales. Sellers must not invoice above that budget without an accepted budget update.

When billing is buyer- or vendor-attested, `report_usage` carries `media_buy_id`, `pricing_option_id`, `commissionable_value`, and `vendor_cost`; the seller verifies the formula against the selected pricing option.

Revenue share is channel-independent. Affiliate is the immediate use case, but sellers may offer this pricing model on other channels where the same commercial contract applies.

***

### Flat Rate

**Fixed cost** - Single payment regardless of delivery volume.

**Use Cases**: Sponsorships, takeovers, exclusive placements, branded content, DOOH fixed slots

**Example** (sponsorship):

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/flat-rate-option.json",
  "pricing_option_id": "flat_rate_usd_sponsorship",
  "pricing_model": "flat_rate",
  "fixed_price": 50000.00,
  "currency": "USD"
}
```

**Example** (DOOH slot with parameters):

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/flat-rate-option.json",
  "pricing_option_id": "flat_rate_usd_dooh_morning",
  "pricing_model": "flat_rate",
  "fixed_price": 5000.00,
  "currency": "USD",
  "parameters": {
    "type": "dooh",
    "daypart": "morning_commute",
    "duration_hours": 4,
    "sov_percentage": 25,
    "slot_span": 2,
    "loop_position": "adjacent_to_content_break",
    "estimated_impressions": 120000
  }
}
```

**Billing**: Fixed cost for the entire campaign period regardless of delivery volume.

**DOOH parameters** (`parameters.type: "dooh"`):

* `sov_percentage`: Guaranteed time share as a percentage (0-100). Compare it with reported `dooh_metrics.sov_achieved` by dividing `sov_percentage` by 100.
* `slot_span`: Number of consecutive loop slots in this seller-packaged offer. It guarantees contiguity, but not position unless `loop_position` is present.
* `loop_position`: Seller-defined loop-position label, such as `any`, `first`, `last`, or `adjacent_to_content_break`. The vocabulary is open; buyers accept unknown values as opaque, with no position guarantee they understand.
* `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)
* `daypart`: Named daypart (e.g., `morning_commute`, `evening_rush`)
* `estimated_impressions`: Audience impression estimate (informational, not a delivery guarantee)

Sponsorship flat\_rate options omit `parameters` — the fixed price covers the placement without DOOH-specific constraints.

Share of voice is always duration-weighted. For a loop with segment durations
of 10, 10, 20, and 40 seconds, reserving the first two consecutive segments is
20 / 80 = 25% SOV, not 2 / 4 = 50%. On equal-duration loops, time share and
slot-count share produce the same result. Delivery reports use the corresponding
0.0-1.0 scale, so a contracted `sov_percentage` of `25` is reconciled against
`dooh_metrics.sov_achieved` of `0.25`.

***

### Time (Cost Per Time Unit)

**Cost per time unit** - Rate scales with campaign duration, enabling self-serve sponsorships.

**Use Cases**: Homepage takeovers, section sponsorships, premium placements where price depends on booking duration

**Example**:

```json theme={null}
{
  "$schema": "https://adcontextprotocol.org/schemas/3.3.0-beta.1/pricing-options/time-option.json",
  "pricing_option_id": "time_usd_daily",
  "pricing_model": "time",
  "fixed_price": 50000.00,
  "currency": "USD",
  "parameters": {
    "time_unit": "day",
    "min_duration": 1,
    "max_duration": 30
  }
}
```

**Billing**: Cost = `fixed_price` x number of `time_unit`s in the campaign flight. For example, a 3-day campaign at \$50,000/day = \$150,000 total.

**Parameters**:

* `time_unit` (required): `"hour"`, `"day"`, `"week"`, or `"month"`
* `min_duration` (optional): Minimum booking duration in time units
* `max_duration` (optional): Maximum booking duration in time units

**Time Unit Calculation**:

| Time Unit | Calculation |
| - | - |
| `hour` | rate x hours in flight |
| `day` | rate x calendar days in flight |
| `week` | rate x weeks (seller-defined rounding) |
| `month` | rate x months (seller-defined pro-rating) |

**Comparison with Flat Rate**:

| Aspect | Flat Rate | Time |
| - | - | - |
| Semantics | Fixed total cost | Rate x duration |
| `fixed_price` means | Total campaign cost | Cost per time unit |
| Buyer flexibility | Must negotiate duration | Self-serve any duration |
| Use case | Fixed sponsorships | Scalable sponsorships |

***

## Digital Out-of-Home (DOOH) Pricing

DOOH advertising uses existing pricing models—typically **CPM** or **flat\_rate**—with optional parameters to describe the inventory allocation.

### Basic Concepts

* **CPM for DOOH**: Priced per thousand impressions, where impressions are calculated based on venue traffic (e.g., Geopath data)
* **Flat rate for DOOH**: Fixed cost for specific duration or allocation (hourly, daily, or exclusive takeover)

### Simple Example: Billboard Takeover

```json theme={null}
{
  "product_id": "billboard_takeover",
  "name": "Premium Billboard - 24 Hour Takeover",
  "pricing_options": [{
    "pricing_model": "flat_rate",
    "fixed_price": 50000.00,
    "currency": "USD"
  }],
  "delivery_measurement": {
    "provider": "Geopath",
    "notes": "Venue traffic data updated monthly. Estimated 2.5M impressions over 24 hours."
  }
}
```

### DOOH parameters (optional)

For flat\_rate DOOH inventory, publishers may include a `parameters` object with `"type": "dooh"`. See the [flat\_rate pricing model](#flat-rate) section above for a complete example and full field list.

**Note**: DOOH measurement and buying practices vary by market. Publishers should clearly explain their measurement methodology and inventory allocation in the product description and `delivery_measurement` field.

***

## Price Breakdown

When buyer and seller have negotiated discounts and commissions, the `price_breakdown` object discloses how `fixed_price` was derived from the rate card. This is optional — sellers who don't need breakdowns omit it entirely, with zero overhead to existing implementations.

`price_breakdown` is a planning-layer construct. It exists on pricing options and confirmed packages within AdCP — it does not propagate into impression-level OpenRTB bid requests or responses.

### Structure

```json theme={null}
{
  "pricing_option_id": "cpm_eur_premium",
  "pricing_model": "cpm",
  "fixed_price": 11.90,
  "currency": "EUR",
  "price_breakdown": {
    "list_price": 14.00,
    "adjustments": [
      { "kind": "discount", "name": "volume", "rate": 0.15, "description": "12x frequency discount" },
      { "kind": "commission", "name": "agency", "rate": 0.15, "description": "Agency commission" },
      { "kind": "settlement", "name": "cash_discount", "rate": 0.02, "description": "2% discount for payment within 10 days" }
    ]
  }
}
```

* `list_price` — the rate card or base price before any adjustments
* `adjustments` — ordered list of adjustments, each categorized by `kind`
* `fixed_price` (on the parent pricing option) — must equal the result of applying all `fee` and `discount` adjustments sequentially to `list_price`. A breakdown from the operator's buying platform also applies `operator_fee` adjustments; see [Operator fees](#operator-fees)

Implementations should ignore unrecognized fields in `price_breakdown` and its adjustments to support forward compatibility. A reader that meets an unrecognized adjustment `kind` SHOULD skip the invariant check rather than report a mismatch.

### Adjustment Kinds

Adjustments fall into five kinds based on their economic effect:

| Kind | Effect on buyer price | Effect on publisher revenue | When applied |
| - | - | - | - |
| `fee` | Increases it | Increases it | Before quoting — walks `list_price` up toward `fixed_price` |
| `discount` | Reduces it | Reduces it | Before quoting — walks `list_price` down toward `fixed_price` |
| `commission` | None — budget includes it | Reduces it (revenue split) | Revenue allocation between intermediary and publisher |
| `settlement` | None — post-invoice | Reduces actual payment | At invoicing or payment time |
| `operator_fee` | Increases it | None — the operator keeps it | Buyer-side only — walks the seller's price up to what the advertiser pays |

**Fees** increase what the buyer pays. Ad serving fees, data/targeting surcharges, and brand safety verification costs are additive to the base price. Without fee adjustments, these would be opaque within `list_price`.

**Discounts** reduce what the buyer pays. Volume discounts, negotiated rates, and early booking discounts all lower both the buyer's cost and the publisher's revenue.

**Commissions** are revenue splits. The buyer's price and budget don't change — commission determines how the payment is divided between the intermediary (e.g., agency) and the publisher. Budgets are always managed inclusive of commissions. Multiple commission adjustments compound sequentially, like discounts — e.g., a 15% agency commission followed by a 5% trading desk fee means the publisher receives `budget × 0.85 × 0.95`. Use `commission` only when the seller has agreed to fund the split; a fee the advertiser pays on top of the seller's price is an [operator fee](#operator-fees).

**Settlement adjustments** are applied at invoicing or payment time (e.g., a cash discount for early payment). They don't affect the committed price or budget. Retroactive rebates and performance incentives are out of scope for `price_breakdown` and would be handled through reconciliation workflows.

**Operator fees** are kept by the account's `operator` out of the advertiser's budget — for example, an agency's 2% insurance fee on every media buy. The advertiser pays it, the operator keeps it, and the seller never sees it. See [Operator fees](#operator-fees).

### Beneficiary

Each adjustment may include an optional `beneficiary` field identifying who receives the adjustment's value. This is most useful for commission adjustments in multi-intermediary chains:

```json theme={null}
{ "kind": "commission", "name": "agency", "rate": 0.15, "beneficiary": "mediaagency.example.com" },
{ "kind": "commission", "name": "trading_desk", "rate": 0.05, "beneficiary": "tradingdesk.example.com" }
```

The value can be a sellers.json domain, an AdCP account ID, or a human-readable party name.

For `operator_fee`, `beneficiary` defaults to the account's `operator`. Set it only when another party receives the fee.

### Invariant

The invariant is: `list_price` with all `fee`, `discount`, and `operator_fee` adjustments applied sequentially equals the buyer's effective price — `fixed_price` when the parent object carries it. Commission and settlement adjustments don't participate — they're disclosed for transparency. If no fee, discount, or operator fee adjustments are present, `fixed_price` must equal `list_price`.

This invariant applies only when `fixed_price` is present on the parent object. For auction-based packages, `price_breakdown` is informational — see [On Confirmed Packages](#on-confirmed-packages).

Fee, discount, and operator fee adjustments compound in array order using these formulas:

```
For rate-based fees:        running = running × (1 + rate)
For amount-based fees:      running = running + amount
For rate-based discounts:   running = running × (1 − rate)
For amount-based discounts: running = running − amount
```

Operator fees use the fee formulas.

All monetary values are rounded to the precision of the currency (e.g., 2 decimal places for EUR/USD) at each step, or, for an option that declares [`price_adjustments`](#selection-dependent-price-adjustments), to that option's price precision, which can be finer. The invariant holds after rounding.

Example with mixed adjustments:

```
list_price: 12.00
  fee, amount: 2.00          → 12.00 + 2.00 = 14.00
  discount, rate: 0.15       → 14.00 × 0.85 = 11.90
fixed_price: 11.90 ✓
```

For the structure example above: 14.00 × (1 − 0.15) = 11.90 ✓ (no fee adjustments in that example)

### Rate vs. Amount

Each adjustment must include exactly one of `rate` or `amount`:

* `rate` — decimal proportion, strictly between 0 and 1 exclusive (0 \< rate \< 1), e.g., 0.15 for 15%
* `amount` — positive number (> 0) in the pricing option's currency

```json theme={null}
{ "kind": "discount", "name": "volume", "rate": 0.15 }
{ "kind": "discount", "name": "negotiated", "amount": 2.00, "description": "Flat rate reduction" }
```

### Budgets and Commission

Budgets are always denominated at the `fixed_price` level, inclusive of commissions. A buying platform's budget toward the advertiser also includes operator fees; see [Operator fees](#operator-fees). If a buyer commits €10,000 at an €11.90 CPM, that €10,000 is the buyer's cost. The agency takes their commission from that amount; the publisher receives the remainder.

This means a buyer agent comparing rates across sellers can use `fixed_price` directly — it's the actual cost per unit, regardless of the underlying commission structure.

### On Confirmed Packages

The `price_breakdown` field also appears on the package response (confirmed line item). On a seller's response it is seller-populated, and buyer agents should treat it as read-only. A buying platform reporting the package to the advertiser may append `operator_fee` adjustments; see [Operator fees](#operator-fees).

**Fixed-price packages** echo the pricing option's breakdown:

```json theme={null}
{
  "package_id": "pkg_12345",
  "product_id": "premium_display",
  "pricing_option_id": "cpm_eur_premium",
  "fixed_price": 11.90,
  "budget": 10000,
  "price_breakdown": {
    "list_price": 14.00,
    "adjustments": [
      { "kind": "discount", "name": "volume", "rate": 0.15, "description": "12x frequency discount" },
      { "kind": "commission", "name": "agency", "rate": 0.15, "description": "Agency commission" },
      { "kind": "settlement", "name": "cash_discount", "rate": 0.02, "description": "2% for payment within 10 days" }
    ]
  }
}
```

**Auction-based packages** use `price_breakdown` to disclose commission and settlement terms against the clearing price. For auction packages, `list_price` is set to the clearing price since there is no rate card derivation to disclose. The discount invariant does not apply — the breakdown is informational only:

```json theme={null}
{
  "package_id": "pkg_67890",
  "product_id": "premium_display",
  "pricing_option_id": "cpm_eur_auction",
  "bidding": { "bid_amount": 12.50 },
  "budget": 10000,
  "price_breakdown": {
    "list_price": 12.50,
    "adjustments": [
      { "kind": "commission", "name": "agency", "rate": 0.15, "description": "Agency commission" },
      { "kind": "settlement", "name": "cash_discount", "rate": 0.02, "description": "2% for payment within 10 days" }
    ]
  }
}
```

### Operator fees

An account's `operator`, typically an agency buying for a brand, often keeps a fee out of the advertiser's budget on every media buy. Neither `fee` nor `commission` fits: a `fee` is paid to the seller, and a `commission` does not change the buyer's price. An `operator_fee` raises the price the advertiser pays and leaves the seller's revenue unchanged. A buying platform's or trading desk's own fee that the advertiser pays on top of the seller's price is also an `operator_fee`, with `beneficiary` naming the platform.

**Who adds it.** Operator fees appear only in breakdowns the operator's buying platform returns to the operator and the advertiser it buys for. The seller never sees them, so sellers MUST NOT include `operator_fee` in a breakdown or `eligible_adjustments` they author.

**Order and the seller's price.** `operator_fee` adjustments MUST follow every `fee` and `discount` adjustment. The running price just before the first `operator_fee` is the seller's price. Commission and settlement adjustments in the same breakdown apply to the seller's price and budget, not the advertiser's. Several operator fees compound in array order.

**Rate basis.** Like a `fee`, an operator fee's `rate` is a markup on the running price. A contract that sets the fee as a share `g` of the advertiser's gross spend converts to `rate = g / (1 − g)` (2% of gross is a rate of about 0.0204), or uses `amount`.

A buying platform reporting a package to the advertiser echoes the seller's breakdown and appends the operator fee:

```json theme={null}
{
  "package_id": "pkg_24680",
  "product_id": "run_of_site_display",
  "pricing_option_id": "cpm_eur_ros",
  "budget": 10200,
  "price_breakdown": {
    "list_price": 10.00,
    "adjustments": [
      { "kind": "discount", "name": "volume", "rate": 0.15, "description": "12x frequency discount" },
      { "kind": "operator_fee", "name": "insurance", "rate": 0.02, "description": "Agency insurance fee" }
    ]
  }
}
```

```
list_price: 10.00
  discount, rate: 0.15       → 10.00 × 0.85 = 8.50   (seller's price)
  operator_fee, rate: 0.02   → 8.50 × 1.02 = 8.67    (advertiser's price)
```

The platform states `budget` at the advertiser's price. The advertiser's €10,200 funds a €10,000 media buy at the seller's €8.50 (seller budget = advertiser budget × seller price ÷ advertiser price), and the operator keeps €200. The platform SHOULD report delivered spend to the advertiser on the same basis. `beneficiary` is omitted because the fee goes to the account's operator. A brand operating its own account has no operator fee to declare.

A flat per-campaign fee or retainer is not a per-unit adjustment and is out of scope. Whether the advertiser sees the operator fee is a matter for the platform's permission model and the operator's contract with the advertiser, not the protocol.

***

## Selection-dependent price adjustments

> [!NOTE]
>
> Experimental (`media_buy.price_adjustments`): sellers that implement `price_adjustments` declare the feature id in `experimental_features`, and the shape may change in 3.x with the notice described in [Experimental status](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/reference/experimental-status.md). The rule that a seller MUST NOT return a table to callers below 3.3 applies in addition.

### Two supported paths

A seller can quote a price in either of two equally supported ways:

* **A fully priced offer.** The seller returns a pricing option, or a configured product or proposal, whose `fixed_price` is the price for what was quoted. It publishes no table. The price applies only to that quote: the buyer cannot derive the price of a different selection, date range, or volume from it and re-discovers or refines to learn it.
* **A fixed price plus an adjustment table.** A fixed-price `cpm`, `vcpm`, `cpc`, `cpcv`, `cpv`, `cpp`, or `flat_rate` option declares an ordered `price_adjustments` table, so the buyer can compute the price locally for the selections and dates the table covers.

Declaring a table is optional and a fully priced offer is not a fallback. Whether a table is present tells the buyer whether it can compute the price itself. Many sellers, broadcast included, price the combination of property, dates, and format against avails and their own yield rules, and a seller managing yield this way SHOULD NOT be expected to publish a table or the method behind its prices. A table suits sellers whose rate really is a base rate with coefficients: Dutch linear TV rate cards multiply a net base price by month, spot-length, target-audience, and market indices.

### Rows and composition

`fixed_price` stays the **base rate**: it is what the buyer pays when no row applies, and it is already net of any negotiated discount in the option's own `price_breakdown`. This is distinct from `eligible_adjustments` (which kinds of adjustment are possible) and from `price_breakdown` (how a negotiated price was derived): `price_adjustments` is the seller's declared, binding function from selections and dates to price.

Every row is a `fee` (adds) or a `discount` (subtracts), carrying exactly one of:

* a **`rate`**: a proportion of the running total at that step. A fee `rate` can be 1 or more, so an index of 2.5 is a fee `rate` of 1.5;
* an **`amount`**: a fixed amount in the option's currency per pricing unit.

There is no separate index row kind. A Dutch index card factor `f` is a fee `rate` of `f − 1` when `f > 1`, a discount `rate` of `1 − f` when `f < 1`, and no row when `f = 1`. The rate form needs no second kind, one rule covers percentage fees, discounts, and index cards, and a row means the same thing wherever it sits in the table.

Each row has a `dimension`. `format_option` and `placement_selection` rows match values the buyer selects; `always` rows need no selection and apply whenever their window allows (month, market, and audience indices).

```json theme={null}
{
  "pricing_option_id": "oranje_grp_eur_2026",
  "pricing_model": "cpp",
  "currency": "EUR",
  "fixed_price": 1000.00,
  "parameters": { "demographic": "A25-54" },
  "price_adjustments": [
    { "dimension": "always", "kind": "fee", "name": "month_index_nov", "rate": 0.10,
      "valid_from": "2026-11-01T00:00:00+01:00", "valid_until": "2026-12-01T00:00:00+01:00" },
    { "dimension": "always", "kind": "discount", "name": "market_index_nov", "rate": 0.05,
      "valid_from": "2026-11-01T00:00:00+01:00", "valid_until": "2026-12-01T00:00:00+01:00" },
    { "dimension": "format_option", "format_option_ref": { "scope": "product", "format_option_id": "spot_20s" },
      "kind": "discount", "name": "spot_length_20s", "rate": 0.20 },
    { "dimension": "always", "kind": "fee", "name": "target_audience_a25_54", "rate": 0.25 },
    { "dimension": "placement_selection",
      "placement_ref": { "publisher_domain": "oranjetv.example", "placement_id": "first_in_break" },
      "kind": "fee", "name": "first_in_break_premium", "rate": 0.20 }
  ]
}
```

A buyer booking a 20-second spot in first position for a flight in the second half of November pays, applying each row to the running total in declared order: `1000.00 → 1100.00` (month index 1.10) `→ 1045.00` (market index 0.95) `→ 836.00` (20-second index 0.80) `→ 1045.00` (audience index 1.25) `→ 1254.00` (first-in-break fee of 20% of the running total, `209.00`). The accepted `price_breakdown` lists the five rows in that order: fee `0.10`, discount `0.05`, discount `0.20`, fee `0.25`, fee `0.20`.

Rate-side spot-length indexing is expressible through [`price_adjustments`](#selection-dependent-price-adjustments) (a row keyed to the spot's format option). Impression-side :30 equivalization and demo-bound CPM remain open and are tracked for 3.4 in [#8001](https://github.com/adcontextprotocol/adcp/issues/8001).

### Eligible dimensions

Selection rows price only values that come from a seller-published catalog on the same product, so the seller can enumerate them in advance. The dimensions in this version are placement and format option (which carries spot length):

| `dimension` | Row selector | Matches when |
| - | - | - |
| `format_option` | `format_option_ref` | The purchase's `format_option_refs` includes the reference. It MUST resolve to a format option the product declares. |
| `placement_selection` | `placement_ref` | A `mode: "selected"` `placement_selection` includes the placement. It MUST resolve to a targetable placement of the product, and the product MUST declare `overlay_support.placement_selection`. |
| `always` | none | Always, subject to the row's window. |

Matching is full-identity equality after resolving both sides to the product's declared entry: a legacy placement ref and a placement identity for the same placement match, a seller-inline placement's `seller_agent.agent_url` compares under the [URL canonicalization rules](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/reference/url-canonicalization.md) rather than byte equality, and the same format option ID under a different `scope` does not. Only values the buyer actually selects match a selection row. Omitting the selection, or choosing the product default, matches no row.

**Property and collection are out of this version.** `property_list` and `collection_list` are buyer-owned references (`agent_url` plus `list_id`): the buyer creates and changes the list, so the seller has no catalog entry to anchor a row to and cannot publish a price for a value it cannot enumerate. A row keyed to a list would be priced by the buyer's own list contents. A seller that prices property or collection differently quotes them as a fully priced offer for the properties or collections the buyer names, or sells them as separately priced products. A later minor version can add property by seller-side id, or collection by `collection_id`, once a row can name a value the seller publishes.

Position in break is modelled as a seller-published placement (the example above prices `first_in_break` that way) until the booking half of [#8009](https://github.com/adcontextprotocol/adcp/issues/8009) lands; the reporting side is a separate contract ([Position pricing is outside this reporting contract](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/media-buy/media-buys/optimization-reporting.md)). Activation method, measurement vendor, and reporting tier have no selectable anchor yet; an unknown `dimension` is a schema error and later minor versions add values as anchors appear.

### Validity windows

A row may carry `valid_from` (inclusive) and `valid_until` (exclusive), both date-times with an offset, so a seller whose month and market indices change monthly publishes one row per window. A row without them always applies. The window is evaluated at the purchase's `start_time`.

A purchase resolves to **one** price, so a flight whose `[start_time, end_time)` crosses a `valid_from` or `valid_until` of a row that would otherwise apply is rejected with [`INVALID_REQUEST`](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/building/verification/compliance-catalog#error-code-invalid-request) naming the flight. The buyer splits the flight into one purchase per window. A flight may end exactly on a `valid_until`. Resolving each installment or each day of one purchase at its own rate would need a price per installment in the accepted snapshot, which this version does not define.

Row windows are separate from quote expiry below: a window says when a rate is in force, expiry says how long the seller holds the quote.

### Resolution

Sellers and buyers MUST resolve a purchase's price in this order:

1. **Select rows.** A row applies when its window contains the purchase's `start_time` and its selector matches. Among rows of one selection dimension, only the first applicable row in declared order fires, so a seller can list the same value once per window and the matching window wins. `always` rows each fire. Sellers who want multi-select purchases priced at the highest applicable row list it first; summing the matched values of one dimension is a recorded limit of 3.3, not supported in this version.
2. **Apply in declared order to one running total.** Start the running total at `fixed_price`. For each firing row, in the order the seller declared it, a `rate` row adds (`fee`) or subtracts (`discount`) `round(running × rate)` and an `amount` row adds or subtracts its `amount`; the result is the new running total. Because each row applies to the running total, a 10% fee followed by a 10% fee gives `× 1.21`, not `× 1.20`; a fixed amount followed by a rate is scaled by the rate; and the same rows declared in another order can resolve to a different price. The seller's declared order is part of the quote.
3. **Precision.** The option's **price precision** is the greatest of the currency's ISO 4217 minor-unit exponent; 4 for `cpm`, `vcpm`, `cpc`, `cpcv`, `cpv`, and `cpp` options; and the fewest decimal places that exactly represent `fixed_price` and each row `amount`. Precision is a property of the numeric values, not of how they were written: `0.0100` and `0.01` are the same value. Every step rounds half away from zero to price precision, so the resolved price is the exact running total and is not rounded again; rounding to currency minor units belongs to later spend and invoice calculation. Implementations MUST use exact decimal or integer arithmetic.
4. **Never negative.** The total MUST NOT fall below zero at the end. Sellers MUST verify at publication that no permitted selection resolves below zero; a purchase that would is rejected with `INVALID_REQUEST`, never clamped.

This replaces the draft's earlier model, which applied index factors multiplicatively and then added percentage premiums to the indexed price without compounding them. The two give the same price for a table that declares its index rows before at most one percentage row and its `amount` rows last, which covers the Dutch example above; they differ when two percentage rows both apply, and when an `amount` row is declared before a rate row.

Two consequences follow from one-row-per-dimension. A multi-select purchase pays the first applicable row's rate on all of its volume, so a seller that wants each selected value priced separately sells separate packages or publishes fully resolved per-selection options. And a row that prices a placement already in the product's default set would make choosing it explicitly cost more than the default, so sellers should price only values that add to the default.

### Quotes stay bound to their terms

A price, whether a fully priced offer or resolved from a table, is bound to what it was quoted for: its scope, applicable dates, buyer commitments, delivery terms, and the account it was quoted to. For a table:

* **Account.** A table applies only to the account it was returned for. A buyer MUST NOT apply a table returned for one account to another account. A response with `cache_scope: "public"` was returned for every account that prices off the public rate card.
* **Expiry.** A table expires with the quote it came in: the proposal's or offer's `expires_at`, or, for a reusable product, the validity of the discovery response it was returned in (see `pricing_version` and `cache_scope` on `get_products`). After expiry the buyer re-discovers and uses the new table; it MUST NOT price a new purchase from the expired one.
* **Without a table.** A fully priced offer carries the same bounds. Its price applies only to what was quoted, and the buyer MUST NOT extrapolate it to other selections or dates.

### Resolved price and accepted terms

* When `pricing` is omitted from a purchase, the seller computes the resolved price. When the buyer supplies `pricing`, the seller MUST reject any `fixed_price` or `price_breakdown` that differs from the resolved result with [`INVALID_REQUEST`](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/building/verification/compliance-catalog#error-code-invalid-request) naming the pricing field, and SHOULD carry the resolved value and breakdown as [`error-details/price-adjustments-mismatch.json`](https://adcontextprotocol.org/schemas/3.3.0-beta.1/error-details/price-adjustments-mismatch.json) (`details.resolved_fixed_price`, `details.resolved_price_breakdown`), so the buyer can see which row it disagrees on. It MUST NOT silently reprice the request.
* In the accepted purchase `pricing`, the seller MUST return `fixed_price` as the resolved rate. When any row fired, the seller MUST also return `price_breakdown`: its `list_price` is the declared option's `list_price` when the option declares a `price_breakdown`, and otherwise the option's declared `fixed_price`; its `adjustments` are the declared breakdown's adjustments followed by one entry per fired row, in declared order. A rate row is recorded with its `rate` when that rate is below 1, and as its currency delta in `amount` otherwise (a fee rate of 1 or more); an amount row is recorded as its `amount`; a row whose delta rounds to zero contributes nothing and is omitted. `list_price` folded through `adjustments` equals `fixed_price`, so the invariant above covers selection-dependent pricing. A verifier re-resolves the table of the accepted `pricing_option_id` over the purchase's selections and flight and compares.
* **The accepted `pricing_option_id` pins the table.** Any change to the table, or to which selections or dates it matches, is a change to binding price and mints a new `pricing_option_id`. So the accepted `pricing_option_id`, which `commercial_terms.terms_digest` covers, identifies exactly one immutable table, unfired rows and rows for other windows included. Changes the agreement already permits (a different format option or placement within the accepted `change_terms[]`) are priced from the table of the accepted `pricing_option_id` without a re-quote. Both parties retain that table from discovery, and the seller MUST price from it, not from a later table the option's successor carries. Quote expiry does not apply once the option is accepted: the accepted ID still names the table.
* **The table grants no change rights.** Pinning the table does not make any selection or date change allowed. Which changes the buyer may make come only from the accepted `change_terms[]` ([DR-0016](https://github.com/adcontextprotocol/adcp/blob/main/governance/decisions/DR-0016-proposal-bound-buyer-change-rights.md)): a change they do not permit returns `ACTION_NOT_ALLOWED` whatever the table would price it at, and a change that exceeds the accepted commercial envelope returns `REQUOTE_REQUIRED`. A seller MUST NOT read a row for an unused value as consent to that change, and a buyer MUST NOT read it as an entitlement.
* **The table is not embedded in the accepted snapshot.** The canonical schemas that carry accepted terms (`canonical-pricing-option.json`, `product-purchase.json`, and the commercial terms that contain them) are closed, and a minor release does not add properties to closed schemas. Embedding the table in the snapshot, so the terms are self-contained without the retained discovery copy, is a 4.0 item tracked with [#8501](https://github.com/adcontextprotocol/adcp/issues/8501).

A seller MUST NOT return an option carrying `price_adjustments` to a caller whose `adcp_version` is below 3.3, because an older buyer would read `fixed_price` as the whole price and be repriced at create; for those callers it publishes fully priced per-selection options or omits the option. A consumer that does not recognize a row's `dimension` treats the option as unpurchasable. The `fixed_price` is net of the option's declared discounts. Auction floors (`floor_price`) do not take adjustments, and `price_adjustments` requires `fixed_price`. Availability and forecasts stay seller-computed: the price resolves locally but feasibility does not.

A seller can instead publish a fully priced pricing option per selection combination, or split the inventory into separately priced products. Both remain valid with equal standing; `price_adjustments` is for sellers whose dimensions are orthogonal enough that a combination catalog would explode and who are willing to publish the coefficients.

***

## Eligible Adjustments

Publishers can declare which adjustment kinds apply to a pricing option using `eligible_adjustments`. This tells buyer agents upfront whether discounts, commissions, or settlement terms are available — before any negotiation begins. Sellers never declare `operator_fee`, which only a buying platform applies.

```json theme={null}
{
  "pricing_option_id": "cpm_eur_standard",
  "pricing_model": "cpm",
  "fixed_price": 14.00,
  "currency": "EUR",
  "eligible_adjustments": ["fee", "discount", "commission", "settlement"]
}
```

When `eligible_adjustments` is present, the buyer knows which kinds of adjustments to expect or request. When absent, no adjustments are pre-declared — the buyer should check `price_breakdown` if present for any adjustments that were already applied.

This field pairs with `price_breakdown`: `eligible_adjustments` signals what is *possible*, while `price_breakdown` shows what was *applied*.

***

## Multi-Currency Support

Publishers can offer the same product in multiple currencies:

```json theme={null}
{
  "product_id": "premium_video",
  "pricing_options": [
    {
      "pricing_option_id": "cpm_usd_guaranteed",
      "pricing_model": "cpm",
      "fixed_price": 45.00,
      "currency": "USD"
    },
    {
      "pricing_option_id": "cpm_eur_guaranteed",
      "pricing_model": "cpm",
      "fixed_price": 40.00,
      "currency": "EUR"
    },
    {
      "pricing_option_id": "cpm_gbp_guaranteed",
      "pricing_model": "cpm",
      "fixed_price": 35.00,
      "currency": "GBP"
    }
  ]
}
```

**Buyer Responsibility**: Buyers must select a currency that the publisher supports.

## Fixed vs. Auction Pricing

### Fixed Pricing (`fixed_price` present)

* Publisher sets a fixed price
* Price is guaranteed and predictable
* Common for guaranteed inventory
* Include `fixed_price` field

### Auction Pricing (`fixed_price` absent)

* Final price determined through auction
* Publisher provides `floor_price` (hard minimum) and optional `price_guidance` (percentile hints)
* Common for non-guaranteed inventory
* Buyer expresses execution intent explicitly with `bidding.bid_amount` or `bidding.max_bid`

The pricing option is the seller's commercial offer; `bidding` is the buyer's execution instruction. The pricing option supplies the auction unit and must use the media-buy currency. Every package budget, minimum-spend target, and canonical bidding amount uses that single denomination; split packages requiring another currency into a separate media buy. In 3.2, pricing-option `max_bid` and package `bid_price` remain only as deprecated migration fields. A seller-offered boolean no longer determines whether buyer input is a manual bid, auction ceiling, or average outcome-cost control.

**Auction Example**:

```json theme={null}
{
  "pricing_option_id": "cpcv_usd_auction",
  "pricing_model": "cpcv",
  "currency": "USD",
  "floor_price": 0.08,
  "price_guidance": {
    "p25": 0.10,
    "p50": 0.12,
    "p75": 0.15,
    "p90": 0.18
  }
}
```

## Buyer Selection Process

Each package specifies its own pricing option, which determines pricing model and auction unit. Its declared currency must match the single media-buy currency:

```json theme={null}
{
  "start_time": "2025-01-01T00:00:00Z",
  "end_time": "2025-01-31T23:59:59Z",
  "brand": {
    "domain": "acmecorp.com"
  },
  "brief": "Q1 Brand Campaign",
  "packages": [{
    "product_id": "premium_ctv",
    "format_option_refs": [{ "scope": "product", "format_option_id": "video_30s" }],
    "pricing_option_id": "cpcv_usd_auction",
    "budget": 50000,
    "pacing": "even",
    "bidding": { "max_bid": 0.16 }
  }]
}
```

**How it works:**

1. Package selects `pricing_option_id` from product (e.g., "cpcv\_usd\_auction")
2. Pricing option determines pricing model, auction unit, and fixed vs auction; its currency must match the media buy
3. Package `budget` and all canonical `bidding` amounts use the media-buy currency
4. Auction-based pricing uses explicit `bidding.bid_amount` for a manual auction bid or `bidding.max_bid` for a hard per-auction ceiling, expressed per the pricing option's unit
5. Sellers reject currency-mismatched packages rather than converting amounts; buyers split them into separate media buys

## Reporting Metrics by Pricing Model

Different pricing models report different primary metrics:

| Pricing Model | Primary Metric | Secondary Metrics |
| - | - | - |
| CPM | impressions | clicks, ctr, spend |
| vCPM | viewable\_impressions | impressions, viewability\_rate, spend |
| CPCV | completed\_views | impressions, completion\_rate, spend |
| CPV | views | impressions, quartile\_data, spend |
| CPP | grps | reach, frequency, spend |
| CPC | clicks | impressions, ctr, spend |
| CPA | conversions | conversion\_value, cost\_per\_acquisition, roas, spend |
| Flat Rate | N/A | impressions, reach, frequency |
| Time | N/A | impressions, reach, frequency |

## Example: Multi-Model CTV Product

A publisher offering Connected TV inventory with multiple pricing options:

```json theme={null}
{
  "product_id": "ctv_premium_sports",
  "name": "Premium Sports CTV",
  "description": "High-engagement sports content on CTV devices",
  "format_options": [
    { "format_option_id": "video_15s", "format_kind": "video_hosted", "params": { "duration_ms_exact": 15000 } },
    { "format_option_id": "video_30s", "format_kind": "video_hosted", "params": { "duration_ms_exact": 30000 } }
  ],
  "delivery_type": "guaranteed",
  "pricing_options": [
    {
      "pricing_option_id": "cpm_usd_guaranteed",
      "pricing_model": "cpm",
      "fixed_price": 55.00,
      "currency": "USD",
      "min_spend_per_package": 15000
    },
    {
      "pricing_option_id": "cpcv_usd_guaranteed",
      "pricing_model": "cpcv",
      "fixed_price": 0.22,
      "currency": "USD",
      "min_spend_per_package": 15000
    },
    {
      "pricing_option_id": "cpp_usd_m18-49",
      "pricing_model": "cpp",
      "fixed_price": 300.00,
      "currency": "USD",
      "parameters": {
        "demographic": "M18-49",
        "min_points": 50
      },
      "min_spend_per_package": 15000
    }
  ]
}
```

A buyer could choose CPP pricing if they're planning TV buys, CPCV if optimizing for engagement, or CPM for reach-based campaigns.

## Negotiated and Premium Pricing

Premium sellers — particularly in OOH, CTV sponsorships, and direct deals — often finalize pricing through negotiation rather than publishing fixed rates. AdCP supports this workflow through the existing brief-to-proposal path without requiring a separate quote or price-on-request mechanism.

### Sellers are not required to publish wholesale prices

Product discovery (`get_products`) does not require sellers to disclose wholesale or clearing prices. Sellers may publish products with limited or no public pricing detail — for example, a CTV sponsorship product with audience and format information but no `pricing_options`. The product is discoverable, but the buyer must submit a brief to receive a proposal with actual pricing.

### Selective brief response

Sellers control which briefs they respond to and what pricing they offer. When a seller receives a brief for inventory they choose not to transact on, the protocol-native options are:

* **Omit the product from the proposal entirely** — the buyer sees no proposal for that inventory, signaling unavailability for this request
* **Return `PROPOSAL_NOT_COMMITTED`** — an explicit signal that the seller reviewed the brief but is not offering a committable proposal at this time

Neither requires the seller to disclose why they declined. This is the protocol-native equivalent of "price not available without negotiation."

### The existing flow covers negotiated deals

The standard path for negotiated-price inventory is:

1. **Seller publishes products** with descriptive metadata (audience, formats, properties) but without wholesale pricing
2. **Buyer submits a brief** via `get_products` describing their campaign goals and budget parameters
3. **Seller returns a proposal** with negotiated pricing tailored to the specific brief — or declines to propose
4. **Buyer refines or accepts** the proposal, then passes the `proposal_id` into `create_media_buy`

This mirrors how premium direct deals work today: the seller sees the brief, decides whether to engage, and offers terms specific to the opportunity.

## Best Practices

### For Publishers

1. **Offer relevant pricing models** - Match pricing to your inventory type and buyer expectations
2. **Set appropriate minimums** - Use `min_spend_per_package` to ensure campaign viability
3. **Provide price guidance** - For auction pricing, give realistic floor and percentile data
4. **Consider multi-currency** - Support currencies of your target markets
5. **Document parameters** - Clearly explain thresholds, demographics, and action types

### For Buyers

1. **Select appropriate model** - Choose pricing that aligns with campaign objectives
2. **Match currency** - Ensure you select a currency the publisher supports
3. **Set realistic budgets** - Account for minimum spend requirements
4. **Align goals with pricing** - Set delivery goals that match your pricing model
5. **Monitor relevant metrics** - Focus on the metrics that matter for your pricing model

## Related Documentation

* [Media Products](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/media-buy/product-discovery/media-products.md) - Product model reference
* [Creating Media Buys](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/media-buy/task-reference/create_media_buy.md) - How to select pricing when buying
* [Delivery Reporting](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/media-buy/task-reference/get_media_buy_delivery.md) - Understanding metrics by pricing model
* [Glossary](https://docs.adcontextprotocol.org/dist/docs/3.3.0-beta.1/reference/glossary.md) - Pricing and metric definitions


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.