Skip to content
Draft
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/draft-booking-rounds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"adcontextprotocol": minor
---

Add experimental optional `Product.booking_rounds[]` (`core/booking-round.json`, `enums/late-request-handling.json`) describing a seller's published booking calendar: airing window (`covers`), `opens_at`, `request_deadline`, `booking_deadline`, `expected_confirmation_at`, and `late_request_handling` with an optional `late_request_pricing_option_id` (#8010). Experimental under feature id `media_buy.booking_rounds`; the shape may change inside 3.x with the usual notice.
37 changes: 37 additions & 0 deletions docs/media-buy/product-discovery/collections-and-installments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,43 @@ The two-stage pattern — draft then final — covers a wide range of channels:

Deadlines are optional. Run-of-collection digital products typically omit them. The pattern is most valuable for guaranteed inventory with advance material requirements.

## Booking rounds (experimental sketch)

<Note>
Draft for WG review (#8010). Not part of AdCP 3.3 unless the working group adopts it. The field names and shape may change.
</Note>

Installment deadlines describe one installment. Some scheduled media is booked in rounds on a published calendar instead: buying opens on a fixed date, a request round closes, and the seller confirms orders later. A request after the round closes may still be accepted, at a different price and without a placement guarantee. A product MAY declare `booking_rounds[]` to publish that calendar.

| Field | Type | Description |
|-------|------|-------------|
| `round_id` | string | Identifier of the round within the product. Required. |
| `covers` | date range | Airing or publication window the round books, for example one month. Required. Calendar dates in the seller's timezone. |
| `opens_at` | datetime | When buying opens |
| `request_deadline` | datetime | Soft close of the request round. Required. |
| `booking_deadline` | datetime | Hard close, same meaning as on an installment. Optional. |
| `expected_confirmation_at` | datetime | When the seller expects to confirm requests received in the round. An estimate, not a commitment. |
| `late_request_handling` | enum | `accepted`, `accepted_without_guarantee`, or `rejected`: treatment of a request after `request_deadline` |
| `late_request_pricing_option_id` | string | Pricing option on the same product that prices an accepted late request |

Ordering: `opens_at` < `request_deadline` ≤ `booking_deadline`, and `request_deadline` ≤ `expected_confirmation_at`. A round's `booking_deadline` MUST NOT be later than the `booking_deadline` of any installment it covers; rounds describe soft closes and the calendar, and hard closes stay with the installment (or `deadline_policy`). Round semantics apply only to a request whose flight falls inside one `covers` window.

When a seller answers `create_media_buy` with a `submitted` response for a request in a round, it SHOULD set [`expected_confirmation_at`](/docs/media-buy/task-reference/create_media_buy#expected-confirmation-time) to the round's `expected_confirmation_at`, so a buyer can schedule a check instead of polling.

```json
{
"$schema": "/schemas/core/booking-round.json",
"round_id": "2026-11",
"covers": { "start": "2026-11-01", "end": "2026-11-30" },
"opens_at": "2026-10-01T08:00:00+02:00",
"request_deadline": "2026-10-09T17:00:00+02:00",
"booking_deadline": "2026-10-28T17:00:00+01:00",
"expected_confirmation_at": "2026-10-23T12:00:00+02:00",
"late_request_handling": "accepted_without_guarantee",
"late_request_pricing_option_id": "cpp_late_booking"
}
```

## Deadline policy

High-frequency collections (daily newspapers, weekly podcasts) would generate large payloads if every installment carried explicit deadlines. The `deadline_policy` on a collection declares lead-time rules that agents use to compute deadlines from each installment's `scheduled_at`.
Expand Down
1 change: 1 addition & 0 deletions docs/media-buy/product-discovery/media-products.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Products declare which pricing models they support. Buyers select a specific pri
- `placements` (list[Placement], optional): Specific public ad placements within this product. Each placement declares `kind` (`publisher_ref` or `seller_inline`) and `mode` (`targetable` or `included`); seller-private delivery objects are not exposed here. See [Placements](#placements).
- `collections` (list[CollectionSelector], optional): Collections covered by this product, grouped by publisher. Each entry has `publisher_domain` and `collection_ids` referencing collections declared in that domain's `adagents.json` — which may be an external channel owner's domain, not the seller's. See [Collections and installments](/docs/media-buy/product-discovery/collections-and-installments).
- `installments` (list[Installment], optional): Specific installments available within this product. See [Collections and installments](/docs/media-buy/product-discovery/collections-and-installments).
- `booking_rounds` (list[BookingRound], optional, experimental): The seller's published booking calendar for this product — per airing window, when buying opens, when the request round closes, when confirmation is expected, and how a late request is treated and priced. Sketch under review (#8010). See [Booking rounds](/docs/media-buy/product-discovery/collections-and-installments#booking-rounds-experimental-sketch).
- `delivery_type` (string, required): Either `"guaranteed"` or `"non_guaranteed"`.
- `exclusivity` (string, optional): Whether this product offers exclusive access. `"none"` (default when absent) — multiple advertisers can buy simultaneously. `"category"` — one advertiser per industry category. `"exclusive"` — sole sponsorship. Most relevant for guaranteed products tied to specific shows or placements.
- `pricing_options` (list[PricingOption], required): Array of available pricing models for this product. A binding option applies uniformly across later values permitted by `overlay_support`; a changed fixed price, floor, currency, model, or priced applicability receives a new `pricing_option_id`. Sellers with value-dependent rates use concretely targeted configured products, separate rate-tier products, or non-binding guidance. See [Pricing Models](#pricing-models).
Expand Down
1 change: 1 addition & 0 deletions docs/reference/experimental-status.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Schemas marked with `x-status: experimental` are the authoritative source. The c
| `media_buy.reporting_delivery` | **Reliable Reporting 1.0**: [`get_reporting_status`](/docs/media-buy/task-reference/get_reporting_status), opt-in [`sync_reporting_status`](/docs/media-buy/task-reference/sync_reporting_status), [`sync_reporting_receipts`](/docs/media-buy/task-reference/sync_reporting_receipts), `sync_accounts.reporting_delivery_configs[]`, reporting notifications, and the `media_buy.reporting_delivery` capability block | Proper-name AdCP 3.2 reporting reliability surface (#6911), explicitly discovered through `reliable_reporting_version: "1.0"`. Core, Managed Delivery, and Reconciled Billing have separate capability-gated conformance storyboards. The [consumer-status migration](/docs/reference/migration/reliable-reporting-status-loop) is an active breaking-change notice: current source adds an opt-in task, which becomes required Core only in the next eligible minor after October 24, 2026. Its `content_mismatch` status, stale-`received` grace window, buyer posting deadline, issue lifecycle fields, escalation contact, and the reserved `authoritative_party` field ([#7440](https://github.com/adcontextprotocol/adcp/issues/7440)) are all additive within that experimental surface. Multi-party production operation and provider-specific compatibility still need implementation experience before freezing. |
| `creative.broadcast_loudness` | The optional `loudness` object (`target_lufs`, `tolerance_db`, `max_true_peak_dbfs`, `standard`) on the `video_hosted` canonical format params (schema: `formats/canonical/video_hosted.json`), documented in [Broadcast creative](/docs/creative/channels/broadcast#loudness-experimental) (#8011) | New broadcast-television delivery requirement with very few adopters so far. The `standard` enum (`ebu_r128`, `atsc_a85`, `custom`) is expected to grow, and the object shape may be reconciled with the flat `loudness_*` fields on `audio_hosted` in a later 3.x release. The `mxf` container and the `mpeg2` / `avc_intra` / `xavc` codec values added alongside it are stable additive enum values, not part of this experimental surface. |
| `media_buy.daypart_granularity` | The `start_time` / `end_time` clock-time fields on `core/daypart-target.json` (and so on every `daypart_targets` entry), the `time_granularity` field in `overlay_support.daypart_targets` and `required_overlay_support.daypart_targets`, and the `enums/daypart-time-granularity.json` enum ([#6707](https://github.com/adcontextprotocol/adcp/issues/6707)). See [Minute-resolution dayparts](/docs/media-buy/advanced-topics/targeting#minute-resolution-dayparts-experimental). | Minute-level schedules for linear TV (programme-junction breaks, overnight broadcast days) arrive with one named adopter and are expected to be reshaped by the network/station targeting work in [#5679](https://github.com/adcontextprotocol/adcp/issues/5679). The `HH:MM` representation, the overnight rule (a window belongs to its start day), the three-value granularity ladder, and the error code for an unsupported granularity (today `UNSUPPORTED_FEATURE`, which may be superseded by the targeting-dimension codes in [#6978](https://github.com/adcontextprotocol/adcp/issues/6978)) may change before they freeze. The whole-hour `start_hour` / `end_hour` fields stay stable, although they are no longer individually schema-required. |
| `media_buy.booking_rounds` | The experimental `Product.booking_rounds` declaration (schemas: `core/booking-round.json`, `enums/late-request-handling.json`) on [`get_products`](/docs/media-buy/task-reference/get_products) and [`list_products`](/docs/media-buy/task-reference/list_products) results (#8010) | Draft sketch of a published booking calendar for scheduled media (linear TV, radio, print) booked in rounds. Two adopters so far; the round shape, how a late request is priced (a pricing option in this sketch; a round-keyed dimension on the selection-dependent `price_adjustments` work in #7659 / #7955 is the 3.4 candidate), and vocabulary shared with `installment.deadlines` and the traffic-cutoff work in #7777 are all open. |

Graduation progress and notice of upcoming breaking changes will be called out in the [release notes](/docs/reference/release-notes) for each 3.x release starting with 3.0 GA.

Expand Down
75 changes: 75 additions & 0 deletions static/schemas/source/core/booking-round.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/core/booking-round.json",
"title": "Booking Round",
"x-status": "experimental",
"x-added-in": "3.3.0",
"description": "One round on a seller's published booking calendar for an airing or publication window: buying opens, a request round closes, and the seller confirms later. Unlike installment.deadlines.booking_deadline (the hard close for one installment), a round also says what happens to a request after request_deadline. Order: opens_at < request_deadline <= booking_deadline; request_deadline <= expected_confirmation_at. booking_deadline MUST NOT be later than that of any installment covered. Applies only when the flight falls inside one covers window. Experimental sketch for WG review.",
"type": "object",
"properties": {
"round_id": {
"type": "string",
"description": "Identifier for this round, unique within the product.",
"x-entity": "booking_round"
},
"covers": {
"$ref": "/schemas/core/date-range.json",
"description": "Airing or publication window this round books, for example one calendar month. Calendar dates in the seller's timezone."
},
"opens_at": {
"type": "string",
"format": "date-time",
"description": "When buying opens for this round (ISO 8601)."
},
"request_deadline": {
"type": "string",
"format": "date-time",
"description": "Soft close of the request round (ISO 8601). A request received after this is governed by late_request_handling."
},
"booking_deadline": {
"type": "string",
"format": "date-time",
"description": "Hard close (ISO 8601), as installment.deadlines.booking_deadline: after it the seller accepts no new requests for this round. When absent, late requests stay open until an installment's own booking_deadline."
},
"expected_confirmation_at": {
"type": "string",
"format": "date-time",
"description": "When the seller expects to confirm requests in this round (ISO 8601). An estimate, not a commitment. A submitted create_media_buy response for a request in this round SHOULD carry this as its expected_confirmation_at."
},
"late_request_handling": {
"$ref": "/schemas/enums/late-request-handling.json",
"description": "Treatment of a request received after request_deadline and before booking_deadline."
},
"late_request_pricing_option_id": {
"type": "string",
"description": "Pricing option on the same product that prices an accepted late request, with the late rate in its own binding price (so it stays inside terms_digest). 3.4 candidate: a round-keyed price_adjustments dimension (#7659 / #7955). Not meaningful when late_request_handling is rejected.",
"x-entity": "product_pricing_option"
},
"ext": {
"$ref": "/schemas/core/ext.json"
}
},
"required": [
"round_id",
"covers",
"request_deadline"
],
"if": {
"properties": {
"late_request_handling": {
"const": "rejected"
}
},
"required": [
"late_request_handling"
]
},
"then": {
"not": {
"required": [
"late_request_pricing_option_id"
]
}
},
"additionalProperties": true
}
10 changes: 10 additions & 0 deletions static/schemas/source/core/product.json
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,16 @@
"$ref": "/schemas/core/installment.json"
}
},
"booking_rounds": {
"type": "array",
"description": "Experimental (`media_buy.booking_rounds`). The seller's published booking calendar for this product, one entry per airing or publication window: when buying opens, when the request round closes, when confirmation is expected, and how a late request is treated. Absence means no published rounds. Does not replace installment.deadlines or deadline_policy.",
"items": {
"$ref": "/schemas/core/booking-round.json"
},
"minItems": 1,
"x-status": "experimental",
"x-added-in": "3.3.0"
},
"enforced_policies": {
"type": "array",
"description": "Registry policy IDs the seller enforces for this product. Enforcement level comes from the policy registry. Buyers can filter products by required policies.",
Expand Down
4 changes: 3 additions & 1 deletion static/schemas/source/core/x-entity-types.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"impairment",
"collection",
"installment",
"booking_round",
"collection_list",
"property_list",
"catalog",
Expand Down Expand Up @@ -162,6 +163,7 @@
"reporting_receipt": "One authenticated consumer reconciliation outcome for an exact reporting materialization.",
"reporting_consumer_status": "One immutable authenticated consumer statement that expected reporting for a configuration period was received, missing, or unreadable. New statements explicitly supersede earlier current state without deleting history.",
"reporting_resource": "One seller-issued secret-free descriptor for an exact reporting materialization, resolved by resource_ref through get_reporting_status. Native platform versions supplement but do not replace this identity.",
"identity_relying_party": "A verified-identity relying party an entity operates for attestation provenance in TMP Identity Match. `relying_party_id` in brand.json identity_relying_parties[] and trusted-match/identity-match-request.json attestation. Namespaced by the issuer (a vendor BrandRef, `core/brand-ref.json`) — identity is the tuple `(issuer.domain, issuer.brand_id, relying_party_id)`, mirroring vendor_metric's `(vendor.domain, vendor.brand_id, metric_id)`; the same string under a different issuer is a different relying party. The publishing owner (whose brand.json lists it) asserts ownership, and the receiver matches a forwarded attestation's `(issuer, relying_party_id)` against the claimed owner's published list; the issuer's own relying-party registry (e.g. World ID on-chain) is the authoritative root. One entity may operate many relying parties (scope=entity vs scope=property) — not 1:1 with an entity."
"identity_relying_party": "A verified-identity relying party an entity operates for attestation provenance in TMP Identity Match. `relying_party_id` in brand.json identity_relying_parties[] and trusted-match/identity-match-request.json attestation. Namespaced by the issuer (a vendor BrandRef, `core/brand-ref.json`) — identity is the tuple `(issuer.domain, issuer.brand_id, relying_party_id)`, mirroring vendor_metric's `(vendor.domain, vendor.brand_id, metric_id)`; the same string under a different issuer is a different relying party. The publishing owner (whose brand.json lists it) asserts ownership, and the receiver matches a forwarded attestation's `(issuer, relying_party_id)` against the claimed owner's published list; the issuer's own relying-party registry (e.g. World ID on-chain) is the authoritative root. One entity may operate many relying parties (scope=entity vs scope=property) — not 1:1 with an entity.",
"booking_round": "One round on a seller's booking calendar for a product (core/booking-round.json), identified by round_id within the product. Experimental."
}
}
19 changes: 19 additions & 0 deletions static/schemas/source/enums/late-request-handling.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/enums/late-request-handling.json",
"title": "Late Request Handling",
"x-status": "experimental",
"x-added-in": "3.3.0",
"description": "What a seller does with a request that arrives after a booking round's request_deadline. Consumers MUST tolerate values added in later minors.",
"type": "string",
"enum": [
"accepted",
"accepted_without_guarantee",
"rejected"
],
"enumDescriptions": {
"accepted": "Accepted with the same placement guarantee as an on-time request. Any late_request_pricing_option_id still selects the price.",
"accepted_without_guarantee": "Accepted, but the seller does not guarantee placement or positioning. The downgrade MUST be visible to the buyer before commitment.",
"rejected": "Not accepted for this round. The seller returns a terminal error, not a submitted task."
}
}
8 changes: 8 additions & 0 deletions static/schemas/source/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@
"$ref": "/schemas/core/installment-deadlines.json",
"description": "Booking, cancellation, and material submission deadlines for an installment"
},
"booking-round": {
"$ref": "/schemas/core/booking-round.json",
"description": "Experimental: one round on a seller's booking calendar (opens, request deadline, expected confirmation, late-request treatment)"
},
"material-deadline": {
"$ref": "/schemas/core/material-deadline.json",
"description": "A deadline for creative material submission at a specific stage"
Expand Down Expand Up @@ -1185,6 +1189,10 @@
"enums": {
"description": "Enumerated types and constants",
"schemas": {
"late-request-handling": {
"$ref": "/schemas/enums/late-request-handling.json",
"description": "Experimental: treatment of a request received after a booking round request deadline"
},
"pricing-model": {
"$ref": "/schemas/enums/pricing-model.json",
"description": "Supported pricing models for advertising products"
Expand Down
Loading