diff --git a/examples/line-item-audio.yaml b/examples/line-item-audio.yaml index 4ca2f21..28b49c5 100644 --- a/examples/line-item-audio.yaml +++ b/examples/line-item-audio.yaml @@ -21,6 +21,8 @@ pricing: commitment_bounds: { min_budget: 5000.00 } commitment: + proposal_id: "examplepub-us-q4-2026-omni" + version: 1 basis: spend amount: 60000.00 currency: USD diff --git a/examples/line-item-ctv.yaml b/examples/line-item-ctv.yaml index fda3994..7cc3d82 100644 --- a/examples/line-item-ctv.yaml +++ b/examples/line-item-ctv.yaml @@ -17,6 +17,8 @@ pricing: commitment_bounds: { min_budget: 25000.00 } commitment: + proposal_id: "examplepub-us-q4-2026-omni" + version: 1 basis: spend amount: 160000.00 currency: USD diff --git a/examples/line-item-display.yaml b/examples/line-item-display.yaml index 808af24..929a346 100644 --- a/examples/line-item-display.yaml +++ b/examples/line-item-display.yaml @@ -62,6 +62,8 @@ pricing: commitment_bounds: { min_budget: 10000.00, max_budget: 500000.00 } commitment: + proposal_id: "examplepub-us-q4-2026-omni" + version: 1 basis: spend amount: 80000.00 currency: USD diff --git a/spec/CHANGELOG.md b/spec/CHANGELOG.md index 1dfacef..1e649d5 100644 --- a/spec/CHANGELOG.md +++ b/spec/CHANGELOG.md @@ -5,6 +5,11 @@ issue that caused it, so the comment record and the spec history line up. ## [Unreleased] +- § 4.1, § 5.6.1 — commitments carry `proposal_id` and `version`; a new + attempt on a stale version, or past `valid_until` or `price_valid_until`, is + rejected, and a replay returns the stored result. Breaking + ([#24](https://github.com/IABTechLab/OpenProposal/issues/24)). + ## [3.0-draft-1] — 2026-09-22 Initial draft published for public comment. Ported from the working-group diff --git a/spec/openproposal-3.0.md b/spec/openproposal-3.0.md index 6940958..62d37d5 100644 --- a/spec/openproposal-3.0.md +++ b/spec/openproposal-3.0.md @@ -181,7 +181,7 @@ items. | Field | Type | Mutability | Description | | :--- | :--- | :--- | :--- | | `proposal_id` | String | immutable | Unique identity for the proposal object | -| `version` | Integer | derived | Monotonically increasing revision number | +| `version` | Integer | derived | Monotonically increasing revision number. Each revision while status is `published` increments it | | `seller_id` | String | immutable | IAB Tech Lab registry reference | | `seller_name` | String | immutable | Canonical legal seller entity name, from the registry | | `type` | Enum | immutable | `standard` \| `custom` | @@ -288,6 +288,8 @@ when status becomes `agreed`. | Key | Required | Meaning | | :--- | :--- | :--- | +| `proposal_id` | REQUIRED | The proposal the commitment is made against | +| `version` | REQUIRED | The proposal `version` the commitment was priced against | | `basis` | REQUIRED | `spend` \| `units` | | `amount` | REQUIRED | The figure committed | | `currency` | REQUIRED when `basis = spend` | ISO-4217 | @@ -296,8 +298,22 @@ when status becomes `agreed`. | `derived_units` | derived | Computed from `amount`, `agreed_rate` and `cost_method`. Informative, not binding | | `idempotency_key` | REQUIRED | Buyer-generated, unique per commitment attempt | -A request carrying an `idempotency_key` already seen MUST return the original -result and MUST NOT create a second commitment. +An `idempotency_key` is opaque and scoped to one buyer and seller pair. The +seller MUST NOT interpret it. + +The seller MUST reject a new commitment attempt whose `version` is not the +current version of the proposal named by `proposal_id`, whose proposal is past +`valid_until`, or whose `pricing[].price_valid_until` has passed. The rejection +MUST return the current version number, so the buyer can fetch it again. These +checks MUST be deterministic, as for `idempotency_key`. + +For a replay from the same buyer of a commitment attempt the seller has +already processed, with the same `idempotency_key` and the same commitment +terms, the seller MUST return the original result and MUST NOT create a second +commitment. The version and expiry checks apply to a new attempt, not to +returning a stored result. Reusing an `idempotency_key` with different +commitment terms MUST produce an explicit conflict and MUST NOT create or +modify a commitment. #### 5.6.2 hold_status @@ -369,7 +385,9 @@ buyer commitment, and `settable` bounds. See ### 6.3 CTV line item -See [`examples/line-item-ctv.yaml`](../examples/line-item-ctv.yaml). +Shows a buyer commitment naming the `proposal_id` and `version` it was priced +against (§ 5.6.1). See +[`examples/line-item-ctv.yaml`](../examples/line-item-ctv.yaml). ### 6.4 Audio line item