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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/revenue-share-media-pricing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"adcontextprotocol": minor
---

Add contingent revenue-share pricing for affiliate and other outcome-priced media.

The new `revenue_share` pricing option applies a decimal `commission_rate` to settled `commissionable_value`. Product discovery can filter fixed, auction, and contingent pricing independently; delivery and `report_usage` expose the commission basis for formula-checked reconciliation.

Revenue-share packages bind billing to an event source and measurement window, do not use `bid_price`, and treat package budget as the maximum payable commission.
10 changes: 8 additions & 2 deletions docs/accounts/tasks/report_usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ description: "report_usage sends consumption data to AdCP vendor agents after ca
testable: false
---

Reports how a vendor's service was consumed after campaign delivery. Called by orchestrators to inform a vendor agent (signals, governance, creative) what was used so the vendor can track earned revenue and verify billing.
Reports how a vendor's service was consumed after campaign delivery. Called by orchestrators to inform a vendor agent (signals, governance, creative) what was used so the vendor can track earned revenue and verify billing. It is also used against the sales agent itself when a media buy names the buyer or a measurement vendor as the billing authority.

Each usage record is self-contained — it carries its own `account` and `media_buy_id`. A single request can span multiple accounts and campaigns.

**Response Time**: ~1s.

**Request Schema**: [`/schemas/v3/account/report-usage-request.json`](https://adcontextprotocol.org/schemas/v3/account/report-usage-request.json)
{/* Add the /schemas/v3/ request link once the revenue-share fields ship in a stable release. */}
**Response Schema**: [`/schemas/v3/account/report-usage-response.json`](https://adcontextprotocol.org/schemas/v3/account/report-usage-response.json)

## Request Parameters
Expand All @@ -34,11 +34,17 @@ Each record requires `account`, `vendor_cost`, and `currency`. Additional fields
| `pricing_option_id` | string | Vendor: Yes | Pricing option from the vendor's discovery response (`get_signals`, `list_creatives`, `list_content_standards`, `list_property_lists`) or execution response (`build_creative`). The vendor uses this to verify the correct rate was applied. |
| `impressions` | number | Signals: Yes | Impressions delivered |
| `media_spend` | number | percent_of_media: Yes | Media spend for percent-of-media cost verification |
| `media_buy_id` | string | Revenue share: Yes | Media buy whose selected package pricing is being reconciled. |
| `conversions` | number | No | Number of attributed conversion events. Optional analytics context for revenue share. |
| `conversion_value` | number | No | Total attributed conversion value. Optional analytics context; not the revenue-share billing basis. |
| `commissionable_value` | number | Revenue share: Yes | Settled attributed value eligible for commission. The seller verifies `vendor_cost = round_currency(commissionable_value × commission_rate)`. |
| `signal_agent_segment_id` | string | Signals: Yes | Signal identifier from `get_signals` |
| `creative_id` | string | Creative: Yes | Creative identifier from `build_creative` or `list_creatives`. Links usage to a specific creative for billing verification. A `build_creative` variant leaf earns a `creative_id` only when trafficked/added to the library — discarded best-of-N or fan-out variants are never reported here; their charge is the inline per-leaf `vendor_cost` on the `build_creative` response (the authoritative record for untrafficked leaves). |
| `build_variant_id` | string | No | When the reported `creative_id` was promoted from a specific `build_creative` variant leaf but differs from that source id, carry the source `build_variant_id` for reconciliation. On the canonical path where `creative_id` is the kept `build_variant_id`, omit this field. |
| `property_list_id` | string | Property lists: Yes | Property list identifier from `list_property_lists`. Links usage to a specific property list for billing verification. |

For revenue-share records, `pricing_option_id` identifies the agreed rate and commission basis. `currency` must match the selected option, and `vendor_cost` must not exceed the package's commission budget.

## Response

| Field | Description |
Expand Down
46 changes: 45 additions & 1 deletion docs/media-buy/advanced-topics/pricing-models.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Pricing Models
description: "AdCP pricing models — CPM, CPCV, CPP, CPC, CPA, time-based, and DOOH pricing. Publisher-declared, buyer-selected model with rate card support."
description: "AdCP pricing models — CPM, CPCV, CPP, CPC, CPA, revenue share, time-based, and DOOH pricing. Publisher-declared, buyer-selected model with rate card support."
"og:title": "AdCP — Pricing Models"
---

Expand Down Expand Up @@ -384,6 +384,50 @@ Buyers should verify the measurement provider meets their campaign requirements

---

### 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

{/* Add $schema once revenue-share-option.json ships through the /schemas/v3/ alias. */}
```json
{
"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
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.

Expand Down
1 change: 1 addition & 0 deletions docs/media-buy/media-buys/optimization-reporting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ Buyers receive the intersection of both. `impressions` and `spend` are always re
- **`completion_rate`**: Completion rate (`completed_views` / `impressions`); `null` when not applicable (e.g. a non-video buy)
- **`conversions`**: Post-click or post-view conversions
- **`conversion_value`**: Monetary value of attributed conversions
- **`commissionable_value`**: Settled portion of attributed conversion value eligible for revenue-share commission. Distinct from `conversion_value` when the agreement excludes taxes, shipping, discounts, returns, cancellations, or ineligible items.
- **`roas`**: Return on ad spend
- **`cost_per_acquisition`**: Cost per conversion
- **`new_to_brand_rate`**: Fraction of conversions from first-time brand buyers
Expand Down
3 changes: 2 additions & 1 deletion docs/media-buy/product-discovery/media-products.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ Publishers declare which pricing models they support for each product. Buyers se
- **CPCV** (Cost Per Completed View) - Cost per 100% video/audio completion
- **CPV** (Cost Per View) - Cost per view at publisher-defined threshold
- **CPA** (Cost Per Acquisition) - Cost per conversion event (purchase, lead, signup, etc.)
- **Revenue Share** - Commission rate applied to settled attributed conversion value
- **CPP** (Cost Per Point) - Cost per Gross Rating Point (TV/audio)
- **Flat Rate** - Fixed cost regardless of delivery volume
- **Time** - Cost per time unit (day, week, month) that scales with campaign duration
Expand All @@ -125,7 +126,7 @@ Each pricing option includes:
}
```

For auction-based pricing (no `fixed_price`), use `floor_price` for minimum bid constraints and optional `price_guidance` for percentile hints. Bid-based auction models (`cpm`, `vcpm`, `cpc`, `cpcv`, `cpv`) may also include `max_bid` as a boolean signal that `bid_price` switches from exact honored price to buyer ceiling mode:
For auction-based unit pricing (no `fixed_price`), use `floor_price` for minimum bid constraints and optional `price_guidance` for percentile hints. Bid-based auction models (`cpm`, `vcpm`, `cpc`, `cpcv`, `cpv`) may also include `max_bid` as a boolean signal that `bid_price` switches from exact honored price to buyer ceiling mode. `revenue_share` is the exception to the old "no fixed price means auction" shortcut: it is contingent pricing, uses `commission_rate`, and never uses `bid_price`.
```json
{
"$schema": "https://adcontextprotocol.org/schemas/v3/pricing-options/cpm-option.json",
Expand Down
3 changes: 2 additions & 1 deletion docs/media-buy/task-reference/get_media_buy_delivery.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,8 @@ The `by_package` array provides per-package delivery details with these key fiel
**Performance**:
- **`pacing_index`**: Delivery pace (1.0 = on track, below 1.0 = behind, above 1.0 = ahead)
- **`rate`**: Effective pricing rate (e.g., CPM)
- **`pricing_model`**: How the package is billed (cpm, cpcv, cpp, etc.)
- **`pricing_model`**: How the package is billed (cpm, cpcv, cpp, revenue_share, etc.)
- **`commissionable_value`**: For `revenue_share`, the settled attributed value to which the selected `commission_rate` applies. Verify `spend = round_currency(commissionable_value × commission_rate)`.

**Accountability**:
- **`missing_metrics`**: Metrics the binding reporting contract advertised but that are not populated in this report. Each entry uses an explicit `scope` discriminator: `{ "scope": "standard", "metric_id": "completed_views" }` for entries from the closed `available-metric.json` enum, `{ "scope": "vendor", "vendor": { "domain": "..." }, "metric_id": "attention_units" }` for vendor-defined metrics. Standard entries MAY carry a `qualifier` mirroring the `committed_metrics` qualifier (e.g., `{ "scope": "standard", "metric_id": "viewable_rate", "qualifier": { "viewability_standard": "mrc" } }` flags a missing MRC commitment even when GroupM viewability was reported, or `{ "scope": "standard", "metric_id": "completion_rate", "qualifier": { "completion_source": "vendor_attested" } }` flags a missing vendor-attested commitment even when seller-attested completion was reported — the paths are not interchangeable). Reconciled against `package.committed_metrics` (filtered to entries where `committed_at < reporting_period.end`) when present; falls back to the product's current `reporting_capabilities.available_metrics` and `vendor_metrics` when absent. Empty array (or absent) indicates clean delivery against the contract; non-empty signals an accountability breach. Sellers MUST exclude metrics that are not yet measurable for the current `measurement_window` (e.g., post-IVT counts during the live window) — those will appear (or not) when a wider window supersedes this report via `supersedes_window`.
Expand Down
5 changes: 3 additions & 2 deletions docs/media-buy/task-reference/get_products.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ The seller must declare `features.property_list_filtering: true` in [`get_adcp_c
| Parameter | Type | Description |
|-----------|------|-------------|
| `delivery_type` | string | Filter by `"guaranteed"` or `"non_guaranteed"` |
| `is_fixed_price` | boolean | Filter for fixed-price vs auction products. Products with both pricing types can match either value, but the returned `pricing_options` array must include only options matching the requested pricing type so buyers can select deterministically from discovery. |
| `is_fixed_price` | boolean | Legacy filter for fixed-price vs auction products. `true` returns options with `fixed_price`; `false` returns options established through `bid_price`. Contingent options such as `revenue_share` match neither value and are omitted whenever this filter is present. |
| `pricing_structures` | string[] | Filter by how the payable price is determined: `fixed`, `auction`, or `contingent`. Sellers return only matching `pricing_options`. Use `contingent` to discover `revenue_share`. When combined with `is_fixed_price`, both filters apply. |
| `pricing_currencies` | string[] | Filter by ISO 4217 currencies the buyer can use for the media product transaction (e.g., `["USD"]`). Products match when they offer at least one product-level `pricing_options` entry in one of the requested currencies and any seller-applied or otherwise mandatory product-scoped signal charges are satisfiable in one of those currencies or have no incremental price. Sellers MUST return only matching product `pricing_options` so buyers can select deterministically from discovery. Optional signal or vendor add-on pricing is not pruned by this filter. |
| `format_ids` | FormatID[] | Filter by specific format IDs |
| `standard_formats_only` | boolean | Only return products accepting IAB standard formats |
Expand Down Expand Up @@ -245,7 +246,7 @@ Use `filters.pricing_currencies` when the buyer's constraint is "only show produ

Buyers MAY send both. Sellers apply them conjunctively: `budget_range.currency` denominates the budget amounts, while `pricing_currencies` narrows which returned product `pricing_options` are eligible. If the two fields conflict, sellers SHOULD return zero matching products rather than reject the request solely because of the conflict. Because product-scoped signal pricing is a separate add-on surface, this filter only gates mandatory seller-applied signal charges; optional signal or vendor add-ons may still advertise other currencies, and buyers should not select unsupported add-on prices.

When combined with `is_fixed_price`, returned product `pricing_options` MUST satisfy both filters: the option must be fixed-price when requested and its `currency` must be in `pricing_currencies`.
When combined with `is_fixed_price` or `pricing_structures`, returned product `pricing_options` MUST satisfy every filter: each retained option must have a requested pricing structure and its `currency` must be in `pricing_currencies`.

Currency-only filter example:

Expand Down
2 changes: 1 addition & 1 deletion docs/protocol/capabilities-explorer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ The largest domain. Sub-namespaces are where most media-buying flags belong.
- **`offline_delivery_protocols`** — supported offline delivery protocols (broadcast trafficking, etc.).
- **`portfolio`** — portfolio-management capabilities.
- **`reporting_delivery_methods`** — how reporting is delivered.
- **`supported_pricing_models`** — array (CPM, CPC, CPCV, CPP, fixed, etc.).
- **`supported_pricing_models`** — array (CPM, CPC, CPCV, CPP, revenue share, flat rate, etc.).

[Propose a flag for `media_buy.features`](https://github.com/adcontextprotocol/adcp/issues/new?title=Add+media_buy+feature:+%3Cflag%3E&body=Schema+location:+%60.media_buy.features%60%0A%0A%23%23+Proposal%0A%0A...%0A%0A%23%23+Why+a+feature+flag+and+not+a+new+task%0A%0A...%0A%0A%23%23+Conformance+probe%0A%0AHow+can+a+buyer+verify+this+capability+is+actually+honored?+...&labels=rfc,capabilities)

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/media-channel-taxonomy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ Affiliate networks, comparison sites, and performance-based publisher partnershi

**Typical Formats**: native, display, text

**Note**: `affiliate` describes the BUYING MODEL (performance-based, CPA/CPC/rev-share) rather than where content appears.
**Note**: `affiliate` describes the BUYING MODEL (performance-based, CPA/CPC/revenue share) rather than where content appears. Percentage-of-sale terms use the [`revenue_share` pricing model](/docs/media-buy/advanced-topics/pricing-models#revenue-share).

#### `product_placement`

Expand Down
3 changes: 2 additions & 1 deletion server/src/addie/mcp/member-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@ const PRICING_ALIASES: Record<string, string> = {
'flat': 'flat_rate', 'flat rate': 'flat_rate', 'sponsorship': 'flat_rate',
'cost per view': 'cpv',
'cost per action': 'cpa', 'cost per acquisition': 'cpa',
'revenue share': 'revenue_share', 'rev share': 'revenue_share', 'commission': 'revenue_share',
};

function normalizeChannel(ch: string): string {
Expand Down Expand Up @@ -6310,7 +6311,7 @@ export function createMemberToolHandlers(
product_id: bestProduct.product_id,
pricing_option_id: bestPricing.pricing_option_id,
budget: liBudget || 0,
...(bestPricing.pricing_model !== 'flat_rate' && liRate ? { bid_price: liRate } : {}),
...(!['flat_rate', 'revenue_share'].includes(bestPricing.pricing_model) && liRate ? { bid_price: liRate } : {}),
...(liStartDate ? { start_time: liStartDate } : {}),
...(liEndDate ? { end_time: liEndDate } : {}),
} : undefined;
Expand Down
Loading
Loading