Repository navigation
Description
What kind of change?
Add a new field
Which section?
Whole document / more than one section
Current text
§ 1.1:
| Direct buy | OpenDirect 2.1 · Order + Line |
| Programmatic guaranteed | OpenDirect 2.1 + Deals API · `deal_id` |
| PMP deal | Deals API + OpenRTB 2.6 · Deal |
| Open market | OpenRTB 2.6 · open auction, no deal |
§ 5.9:
| `deal_id_issuance` | String | derived | When and how the deal id is minted. Issued at commitment time |
| `buyer_requirements` | Object | mixed | `{ seat_required, dsp_allowed[] }` are seller-set; `{ dsp_seat_id, dsp_id, seat_owner_entity }` are supplied |
§ 5.10:
| `external_references[]` | Array[Object] | derived | `{ system, namespace, id, role }`. Replaces platform-specific key names. A buyer-owned reference such as a Prisma `io_id` is supplied |Proposed text
§ 1.1, replace the table with one row per `transaction_mechanism` (§ 5.9):
| `transaction_mechanism` | Downstream standard | `reference_type` (§ 5.10) |
| :--- | :--- | :--- |
| `direct_io` | OpenDirect 2.1 · Order + Line | `order`, `line` |
| `programmatic_guaranteed` | OpenDirect 2.1 · Order + Line, as the reservation; Deals API 1.0 · `Deal` | `order`, `line`, `deal` |
| `preferred_deal` | Deals API 1.0 · `Deal` + OpenRTB 2.6 · `Deal` | `deal` |
| `private_auction` | Deals API 1.0 · `Deal` + OpenRTB 2.6 · `Deal` | `deal` |
Buying in the OpenRTB 2.6 open auction is outside this specification: with
no deal ID, no bid request can name a line item.
**Deals API handoff.** A line item agreed under a deal mechanism (§ 5.9)
becomes a Deals API 1.0 `Deal`. The system named by `deal_origin` (§ 5.9)
pushes the `Deal` to the DSP named by `buyer_requirements.seats.dsp`. The
deal ID is in that system's namespace: it assigns the ID, or accepts one the
seller supplies. The `Deal` takes these values from the agreed line item:
| Deals API 1.0 | OpenRTB 2.6 | From the agreed line item |
| :--- | :--- | :--- |
| `id` | `Deal.id`, `Bid.dealid` | None: the ID in `deal_origin`'s namespace. The `external_references[]` entry with `reference_type: deal` records it (§ 5.10) |
| `origin` | | `deal_origin` |
| `seller` | | The proposal's `seller.domain` |
| `wseat` | `Deal.wseat` | `buyer_requirements.seats.ids[]` |
| `adtypes` | | From `channel`: `display` 1, `video` 2, `audio` 3, `native` 4, and `ctv` 2 when `placement.plcmt` is present. Otherwise unset: the record doesn't state the ad's type |
| `terms.startdate`, `terms.enddate` | | `commitment.flight.start`, `commitment.flight.end` |
| `terms.countries` | | The countries of `geo` or `markets` in effect, in alpha-3 (below), without a country excluded whole. Unset when the line item declares neither |
`commitment.flight.end` and `terms.enddate` are the flight's last second,
inclusive. The Deals API 1.0 doesn't say whether `enddate` is inclusive, so
this mapping states it.
`terms.countries` takes the ISO 3166-1 alpha-3 code of each country. The
Deals API 1.0 says "ISO-3166-3", and this mapping reads that as alpha-3. The
list names whole countries, so `US` minus `US-TX` sends `USA`, and `geo` in
the agreed record holds the exclusion.
The line item's `status` ends at `agreed` (§ 5.1). After the push, the deal's
state is the Deals API `sellerstatus` and, for each seat, `BuyerStatus.status`.
OpenProposal has no field for either.
§ 5.9, replace the `deal_id_issuance` row:
| `deal_origin` | RegistrableDomain | seller-set | REQUIRED when the line item offers a deal mechanism; absent otherwise. The advertising system domain whose namespace holds the deal ID and that pushes the deal, as the Deals API 1.0 `origin`: typically an SSP, or the ad server for a deal it books itself |
§ 5.9, `buyer_requirements`: replace `dsp_id` and `dsp_seat_id` with the
supplied `seats`, a `SeatRef { dsp, ids[] }`. `dsp` is the DSP's primary
domain. `ids[]` holds the buyer's seats on the deal, as the Deals API 1.0
`wseat`: the ids the DSP issues, the same values OpenRTB 2.6 carries in
`Deal.wseat`. A seat always travels with its DSP. `seat_required` becomes
`seats_expected`. `dsp_allowed[]` becomes `allowed_dsps[]`, each entry a
DSP's primary domain. `seat_owner_entity` becomes `seat_owner`, a `Party`
(absent: the `buyer`).
§ 5.10, `external_references[]` row, append:
Once the deal exists in `deal_origin`, the seller MUST record its ID as one
`deal` entry on the line item. An entry is not a term and MAY be added after
`agreed`.
§ 5.10, `ExternalReference`: `role` becomes `reference_type`. Its core term
`deal`: the deal ID that bid requests carry, with `system` set to
`deal_origin`. It is the Deals API `id` and the OpenRTB 2.6 `Deal.id`,
whether `deal_origin` assigned it or accepted it from the seller. An ID that
bid requests don't carry, such as the origin's internal record ID, is not a
`deal` entry.Why
The handoff to the Deals API has no key. We ask OpenProposal to carry the deal as the Deals API defines it: its ID, the system whose namespace holds it, and the buyer's seats.
§ 1.1 routes programmatic guaranteed through "Deals API · deal_id". The Deals API 1.0 Deal has no deal_id. It has id, "in origin's namespace", which should match the OpenRTB deal.id. OpenProposal has no field for the ID either: deal_id_issuance describes minting in free text.
@franks33 (#124) asks for the minted deal ID in external_references[] at agreed, with named roles. Their follow-up wants no "two unconnected ledgers". IAB Tech Lab's reference seller agent already does this. On its unmerged feat/openproposal-mapping branch, the Ad Manager mapper writes an external_references[] entry with role: deal, noting that deal_id_issuance "only says how ids are minted". We make that the rule, and name system by the Deals API origin. A buyer then joins its agreed record, the push, and every bid on one ID.
The Deals API names who pushes: the required origin. The ID is in its namespace, and implementers use "the Deal ID from the Origin system that did the PUSH". A buyer needs it before committing, to check that its DSP has a seat there. deal_origin replaces deal_id_issuance, which @VJAlkimi (#35) lists as a seller or SSP assertion, not a derived value. Timing needs no field. Our binding-record change request (#63) keeps the ID outside the binding record, so recording it after agreed changes no term. Print, in the MVFP print channel request (#102), has no deal and omits deal_origin.
The Deals API wseat is an array, and BuyerStatus accepts or rejects each seat. dsp_seat_id holds one seat, so an agency buying on two seats can't say so. A seller maps dsp_id to a DSP by hand, as @dtbarne shows on #2.
Status needs a boundary, not new values. Our agreed-baseline comment (#68) ends OpenProposal's lifecycle at agreed, and the Deals API sellerstatus starts there.
stateDiagram-v2
direction LR
state "OpenProposal line item" as OP {
agreed
}
state "Deals API 1.0 Deal" as DA {
deal : sellerstatus, BuyerStatus.status
}
OP --> DA : lands in "Deals API · deal_id"
note right of DA : neither side has a deal_id field, and nothing names the origin
Proposed:
stateDiagram-v2
direction LR
state "OpenProposal line item" as OP {
agreed
}
state "Deals API 1.0 Deal, pushed by deal_origin" as DA {
seller : sellerstatus
buyer : BuyerStatus.status, per seat in wseat
}
OP --> DA : push, id recorded as external_references[] reference_type deal
note left of OP : lifecycle ends at agreed
Details: Deals API alignment: full crosswalk
Knock-on effects
SeatRefandRegistrableDomaincome from our shared types PR. Thebuyer_requirementsrenames and the eligibility rules come from our parties PR, andreference_typefrom our post-agreement PR.- Our deal price change request adds the
terms.dealfloor,terms.cur,terms.pricetype,terms.guar,terms.units, andterms.totalcostrows to the handoff table. - The
sellerrow usesseller.domainfrom our parties PR, which closes our seller identity change request (#61). - The flight rows need UTC instants, as @EvgenyAndroid's flight issue (#127) and @VJAlkimi's examples comment (#37) ask, with an inclusive end. Our shared types PR adds
commitment.flight. deal_id_issuancedrops out of [Change] § 4.1 — Define record_digest and assent[]; defer signer identity to the AAMP profile #63's exclusion list, [Change] § 4.1 — Freeze a version's offered terms; changes create version n+1 and a revised entry #64's non-offered terms, and [Comment] § 1.1, § 4.1, § 5.1 — Lifecycle ends at agreed: say so, name the baseline #68's list of non-terms.deal_originis an offered term, inside the binding record.
Open questions for the working group
- Does the curator's own deal ID, Deals API
curation.cdealid, need a corereference_typeterm besidedeal, withsystemset to thecuratordomain? Or is an extension term enough? OpenProposal has no curator field. @Sirajmx's cross-publisher composition (#28) names the original seller withsource_ref. - A seller may be able to set one line item up on several SSPs. Does it publish one line item per
deal_origin? Or isdeal_originselectable (§ 3.1), so the buyer picks the SSP where its DSP has a seat? - Answered in our handoff PR, by @franks33: yes, in
ext.baseline, withseller_domain. Should the push carry the agreed baseline, aVersionRef(proposal_id,version,line_item_id) with the proposal'sseller.domain? A DSP could then join a deal to its agreed record without the buyer's agent. The Deals API has no field for it exceptext. Our agreed-baseline comment (#68) names that baseline, and our post-agreement PR types it. @dineshbhat's comment on it asks which Deals API operation carries a revised deal.
Would this break an implementation already built to this draft?
Yes — breaking
Alternatives you considered and rejected
- A
deal_idfield on the line item.external_references[]already holds IDs minted elsewhere, and [Change] § 4.1 — Define record_digest and assent[]; defer signer identity to the AAMP profile #63 already keeps them out of the binding record. - A
systemvocabulary, as [Comment] § 5.10 — Require named external_references at agreed for agent proposal and deal ids #124 suggests. For this role theorigindomain names the system without a registry. serving_platformas the origin. It names where the line item serves. It can equal the origin, as for a deal booked in Ad Manager, but not for a deal sold through a separate SSP.- Mirroring
sellerstatusin OpenProposalstatus. Two records of one state drift apart. [Comment] § 1.1, § 4.1, § 5.1 — Lifecycle ends at agreed: say so, name the baseline #68 keeps one.
You are commenting as
Other — IAB Tech Lab member (Postindustria).
Before you submit
- I have read CONTRIBUTING.md and agree my contribution may be published under this repository's licence.
- This contains no confidential or commercially sensitive information.
Revised 2026-11-05: Question 3 is answered. Uses the coming PRs' names, and the handoff PR closes this issue. Replaced fields are removed, not deprecated.
Analyzed and generated with the help of Claude, fully reviewed by me.
Activity