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
5 changes: 5 additions & 0 deletions .changeset/add-seller-optimized-media-buy-budgets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"adcontextprotocol": minor
---

Add seller-optimized shared budgets and explicit bidding policy placement across media-buy packages. Media buys and proposals can delegate cross-package allocation to the seller under an aggregate budget, optimization goals, pacing, package caps, and soft minimum-spend targets, while preserving fixed package budgets as the default. A new media-buy/package `bidding` block separates objective functions from automatic bidding, manual bids, auction ceilings, average-cost controls, and ROAS controls, with complete-block inheritance and authored-scope-preserving readback. Media-buy outcome controls bind to allocation goals in seller-optimized mode and compatible package goals in fixed mode; package controls bind to package goals. All canonical monetary fields use one media-buy currency, ROAS event sources declare supported value currencies, and structured capabilities advertise support by scope, allocation context, mode, strength, and combination.
23 changes: 11 additions & 12 deletions docs/media-buy/advanced-topics/pricing-models.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ Buyers should verify the measurement provider meets their campaign requirements
"pricing_model": "cpc",
"currency": "USD",
"floor_price": 0.50,
"max_bid": true,
"price_guidance": {
"p50": 1.20,
"p75": 2.00
Expand Down Expand Up @@ -582,7 +581,7 @@ The value can be a sellers.json domain, an AdCP account ID, or a human-readable

The invariant is: `list_price` with all `fee` and `discount` adjustments applied sequentially equals `fixed_price`. Commission and settlement adjustments don't participate — they're disclosed for transparency. If no fee or discount 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 (where only `bid_price` exists), `price_breakdown` is informational — see [On Confirmed Packages](#on-confirmed-packages).
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 and discount adjustments compound in array order using these formulas:

Expand Down Expand Up @@ -655,7 +654,7 @@ The `price_breakdown` field also appears on the package response (confirmed line
"package_id": "pkg_67890",
"product_id": "premium_display",
"pricing_option_id": "cpm_eur_auction",
"bid_price": 12.50,
"bidding": { "bid_amount": 12.50 },
"budget": 10000,
"price_breakdown": {
"list_price": 12.50,
Expand Down Expand Up @@ -732,9 +731,10 @@ Publishers can offer the same product in multiple currencies:
### Auction Pricing (`fixed_price` absent)
- Final price determined through auction
- Publisher provides `floor_price` (hard minimum) and optional `price_guidance` (percentile hints)
- Bid-based auction models (`cpm`, `vcpm`, `cpc`, `cpcv`, `cpv`) may also include optional `max_bid: true` to explicitly signal `bid_price` is treated as buyer maximum willingness to pay (ceiling mode)
- Common for non-guaranteed inventory
- Buyer submits `bid_price` in media buy request
- 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
Expand All @@ -743,7 +743,6 @@ Publishers can offer the same product in multiple currencies:
"pricing_model": "cpcv",
"currency": "USD",
"floor_price": 0.08,
"max_bid": true,
"price_guidance": {
"p25": 0.10,
"p50": 0.12,
Expand All @@ -755,7 +754,7 @@ Publishers can offer the same product in multiple currencies:

## Buyer Selection Process

Each package specifies its own pricing option, which determines currency and pricing model:
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
{
Expand All @@ -771,17 +770,17 @@ Each package specifies its own pricing option, which determines currency and pri
"pricing_option_id": "cpcv_usd_auction",
"budget": 50000,
"pacing": "even",
"bid_price": 0.16
"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 currency, pricing model, and fixed vs auction
3. Package `budget` is in the pricing option's currency
4. Auction-based pricing requires `bid_price`; it is the exact honored price unless `max_bid: true`, which switches it to a maximum-willingness-to-pay ceiling
5. Sellers validate currency compatibility across packages
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

Expand Down
9 changes: 6 additions & 3 deletions docs/media-buy/conversion-tracking/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ This shows the recommended order. In practice, media buys can be created before

An event source represents a channel through which conversion or marketing events are collected — a website pixel, mobile SDK, server-to-server integration, CRM import, or seller- or platform-native owned property such as a channel, profile, feed, podcast, or newsletter list.

Configure event sources with [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources). You provide an `event_source_id`, optional `name`, `event_types`, `action_source`, `surface`, and `allowed_domains`. The response includes additional fields for each source:
Configure event sources with [`sync_event_sources`](/docs/media-buy/task-reference/sync_event_sources). You provide an `event_source_id`, optional `name`, `event_types`, `value_currencies`, `action_source`, `surface`, and `allowed_domains`. `value_currencies` is required when the source will feed canonical ROAS. A multi-currency source may list several currencies; each buy consumes only records matching its media-buy currency, with no conversion. The response includes additional fields for each source:

| Field | Type | Description |
|-------|------|-------------|
| `seller_id` | string | Seller-assigned identifier in their ad platform |
| `action` | string | What happened: `created`, `updated`, `unchanged`, `deleted`, `failed` |
| `managed_by` | string | `buyer` (you configured it) or `seller` (always-on, seller-managed) |
| `value_currencies` | string[] | Currencies accepted for monetary event values and eligible for canonical ROAS |
| `action_source` | [ActionSource](#action-sources) | Flat source type for legacy and broad compatibility (website pixel, app SDK, in-store, etc.) |
| `surface` | [EventSurface](#event-surfaces) | Optional structured surface, such as an owned channel, profile, feed, podcast, newsletter list, website, app, or store |
| `setup` | object | Implementation details — snippet code, snippet type, instructions |
Expand Down Expand Up @@ -148,7 +149,7 @@ Send multiple identifier types when available. The seller uses the best availabl

## Custom data

Event-specific data for attribution and reporting. For purchase events, always include `value` and `currency` to enable ROAS reporting.
Event-specific data for attribution and reporting. Sources used for canonical ROAS declare `value_currencies`; monetary records from those sources require `currency` from that list. Legacy sources without the contract remain valid for non-ROAS use.

```json
{
Expand Down Expand Up @@ -444,7 +445,9 @@ A `maximize_value` target steers spend toward higher-value conversions without c
| `target.kind` | `"cost_per"` \| `"per_ad_spend"` \| `"maximize_value"` | No | Target type. When omitted, the seller maximizes conversion count within budget (see [default behavior](#default-behavior-for-event-goals)). |
| `target.value` | number | Yes (if target set) | Cost per event in buy currency, or return ratio (e.g., 4.0 = \$4 per \$1 spent) |
| `attribution_window` | object | No | Click-through and view-through windows. When omitted, the seller uses their default. |
| `priority` | integer | No | 1 = highest priority. When omitted, sellers use array position. |
| `priority` | integer | No | Lower values rank first. Ties use array order; unprioritized goals follow explicitly prioritized goals. |

For canonical `bidding.roas`, every value-bearing source must declare the media-buy currency in `value_currencies` before buy creation. Monetary `log_event` records require `custom_data.currency` from the source's declared list. A USD buy uses only USD records; EUR records from the same source remain available to EUR buys. Sellers never convert value currencies.

### kind: metric

Expand Down
6 changes: 4 additions & 2 deletions docs/media-buy/media-buys/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Transform discovered products into active advertising campaigns using [`create_m

- **Package Configuration**: Combine products with formats, targeting, and budget
- **Campaign Setup**: Define timing, overall budget, and brand context
- **Execution Policy**: Define media-buy bidding defaults and explicit package overrides separately from optimization goals
- **Validation & Approval**: Automated checks with optional human approval
- **Platform Deployment**: Campaign creation across advertising platforms

Expand Down Expand Up @@ -75,6 +76,7 @@ For complete details on optimization strategies, performance monitoring, standar
A media buy contains:
- **Campaign metadata** (buyer reference, brand, timing)
- **Overall budget** with currency and pacing preferences
- **Bidding policy** at media-buy scope, with optional complete package overrides and authored-scope-preserving readback
- **Multiple packages** representing different targeting/creative combinations
- **Status tracking** through creation, approval, and execution phases

Expand All @@ -96,7 +98,7 @@ Packages are the building blocks of media buys:
- **Single product** selection from discovery results - when you buy a product, you buy the entire product (unless using property targeting)
- **Creative formats** to be provided for this package
- **Targeting overlays** for refinements including geo restrictions, frequency caps, and property targeting
- **Budget allocation** as portion of overall media buy budget
- **Budget controls** as an independent fixed budget or a cap/soft minimum within seller-optimized allocation
- **Pricing option** selection from product's available pricing models
- **Pacing strategy** for budget delivery (even, asap, or front_loaded)
- **Bid price** for auction-based pricing models (when applicable)
Expand All @@ -109,7 +111,7 @@ Packages can have independent flight dates within a media buy. This enables week

- **Inheritance**: When `start_time` or `end_time` is omitted on a package, the package inherits the media buy's dates. Each field inherits independently — a package may specify `start_time` while inheriting the media buy's `end_time`, or vice versa.
- **Validation**: Package dates must fall within the parent media buy's date range. Sellers SHOULD reject packages where `start_time` is equal to or after `end_time`.
- **Overlapping flights**: Multiple packages for the same product may have overlapping date ranges. Each package maintains its own independent budget.
- **Overlapping flights**: Multiple packages for the same product may have overlapping date ranges. In fixed mode each package maintains an independent budget; in seller-optimized mode the packages draw from the shared total within their constraints.
- **Format**: Plain ISO 8601 date-time — packages do not support `"asap"`

**Weekly flights example:**
Expand Down
14 changes: 12 additions & 2 deletions docs/media-buy/media-buys/optimization-reporting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,17 @@ The two flows aren't a duplication — they encode the difference between *stand

### Required coherence between layers

Three rules connect the layers and prevent orphaned goals:
`optimization_goals` define the objective function: what the seller should optimize, its units, and priority. They do not define 3.2 bidding policy. Currency-bearing execution controls live in `bidding` at media-buy or package scope. All monetary amounts use the media-buy currency; a selected pricing option contributes the auction unit but MUST declare that same currency:

- seller-optimized media-buy `cost_per`/`roas` bind to the primary `budget_allocation.optimization_goals` goal;
- package-authored `cost_per`/`roas` bind to that package's primary goal;
- fixed media-buy `cost_per` binds to every inheriting package and requires compatible primary-goal result units; fixed inherited `roas` requires value-bearing primary goals;
- `bid_amount` and `max_bid` are expressed per the selected pricing option's auction unit; and
- `{automatic: true}` explicitly overrides inherited policy with provider automatic bidding.

The separation matters when the units differ—for example, a seller can optimize toward conversions using a target CPA while also enforcing a max-CPC auction ceiling, with both amounts denominated in the media-buy currency. Legacy goal targets `cost_per` and `per_ad_spend` remain accepted during 3.2 migration but cannot be combined with canonical `bidding` on the same effective package. Their conservative normalization remains `cost_per` → `strength: "target"` and `per_ad_spend` → `strength: "target"`.

Four rules connect the layers and prevent orphaned goals:

1. **Optimization requires capability.** A package's `optimization_goals[]` entry MUST match a product capability — `metric_optimization.supported_metrics` for `kind: "metric"`, or `vendor_metric_optimization.supported_metrics` for `kind: "vendor_metric"`. Sellers reject mismatches with `TERMS_REJECTED`.

Expand All @@ -85,7 +95,7 @@ Three rules connect the layers and prevent orphaned goals:
- Standard only — "you owe me ≥ X," seller decides how to hit it
- Both — optimization steers; standard backstops

4. **Priority is ordinal, lowest-value-first.** When `optimization_goals[]` carry explicit `priority` values, the seller treats the goal with the **lowest** priority value as primary — even when no goal is `priority: 1` (priorities of `2` and `3` mean `2` is primary). When `priority` is omitted, sellers may use array position. Duplicate priority values are undefined.
4. **Priority is ordinal, lowest-value-first.** When `optimization_goals[]` carry explicit `priority` values, the seller treats the goal with the **lowest** priority value as primary — even when no goal is `priority: 1` (priorities of `2` and `3` mean `2` is primary). When all priorities are omitted, the first array entry is primary. Duplicate priority values are undefined.

### Worked example: Adelaide attention end-to-end

Expand Down
Loading
Loading