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? - Should the push carry the agreed baseline, a
VersionRef(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-03: Uses the coming PRs' names, and the handoff PR closes this issue. Replaced fields are removed, not deprecated. A DSP is named by its domain. Adds adtypes and terms.countries rows.
Analyzed and generated with the help of Claude, fully reviewed by me.
Activity