Repository navigation
§ 1.1, § 5.4, § 5.9 — Handoff to OpenDirect and the Deals API (#132, #133) - #145
KonstantinMirin wants to merge 48 commits into
Conversation
§ 5.6 defines floor as the seller's offer floor at proposal time, not the downstream auction floor and not a guarantee that bids will clear. Editorial: the meaning of the field is unchanged. Refs IABTechLab#16
A proposal can say its line items must be bought together (linked_execution, § 4.1), and a line item can name the line items or spend that must be in the same commitment (requires, § 5.6.3). A bonus line is a line item with requires and its own price. The § 2 "binding terms" hook now points at a field, and the Summary Representation carries has_requires and linked_execution. Adds a CTV bonus line example (§ 6.5) and linked_execution: none to the proposal example. Refs IABTechLab#23
The seller declares which exclusion types it accepts and how it
enforces each (exclusions_accepted); the buyer supplies its lists
(exclusions). Blocked apps carry their store, { platform, bundle },
so one store id is never applied across stores. Exclusions are
recompute inputs (§ 3.3) and are never disclosed to other parties
(§ 5.8). The display example declares and supplies lists.
Refs IABTechLab#25
A line item whose rate differs by market can declare geo with the selectable structure of § 3.1: available[], included[], pricing deltas and max_select. A geo that declares only allowed[] stays settable, so existing payloads are unchanged. When several priced markets are selected, the highest delta applies. The audio example is priced by market. Refs IABTechLab#14
A commitment names the proposal and version it was priced against (§ 5.6.1). The seller rejects a new attempt on a stale version, or past valid_until or price_valid_until, and returns the current version. An authenticated replay with the same idempotency_key and terms returns the stored result without re-running those checks; the same key with different terms is a conflict. Keys are opaque and scoped to one buyer and seller pair. § 4.1 states that each revision while published increments version. The display, CTV and audio examples carry proposal_id and version. Refs IABTechLab#24
§ 4.1 adds an optional, immutable proposal_template_id linking an issued proposal to the seller's template. Each issued proposal has its own status, version and negotiation_history[], and buyers never need the template id to transact. § 7 adds a non-normative note separating seller-side templates from the issued proposals buyers commit against. Appendix A gains one row; Appendix B counts immutable 6 → 7 and 72 fields. The proposal example carries a proposal_template_id. Refs IABTechLab#26
§ 5.8 adds terms_ref ({ url, sha256, version }), REQUIRED when
terms_acceptance_required is true, hash-pinned and frozen at agreed, so
assent binds to an exact document. § 5.10 adds agreement_ref, an
informative pointer to the master or annual agreement a line item is
priced under, and documents the io and agreement roles on
external_references[]. IO generation is stated to be a connector
concern, and § 7 adds a non-normative note on relationships.
agreement_ref is seller-set; a buyer-held agreement id travels as an
external_references[] entry with role: agreement, so the field keeps
one marker (§ 3).
Appendix A gains two rows; Appendix B counts seller-set 29 → 31 and
73 fields. The display example carries terms_ref.
Refs IABTechLab#27
A new § 2.3 lets a seller publish its public standard line items at /.well-known/openproposal.json: seller_id, generated_at, spec_version and, per proposal, proposal_id, version, the Summary Representation of each line item and a full_url. ETag / Last-Modified are required, only line items with visibility public appear, and entries must match the current version. Buyers discover from the manifest, then fetch the Full Representation of shortlisted line items. Adds examples/manifest.json (§ 6.5) and visibility: public to the CTV and audio examples; the registry_gated display line item is not listed. Refs IABTechLab#29
The specification does not define authentication yet (WS1; see IABTechLab#62), so the replay rule now applies to a replay from the same buyer rather than an authenticated replay. Refs IABTechLab#24
agreement_ref.parties used buyer_id, which the specification does not define. parties[] now names the seller by its seller_id and the buyer by the id it holds in eligible_bidders[] (section 5.8). Refs IABTechLab#27
The rule that a hold on a linked_execution: all proposal uses hold_scope: proposal came from a comment on IABTechLab#23, not the change request as filed. It moves to the PR description as a point for the working group, so this PR carries only the filed change. Refs IABTechLab#23
IABTechLab#25 names the content-category list iab_content_category[], a list of IAB Content Taxonomy ids. The earlier commit renamed it content_category with a version-pinned taxonomy_ref, which nobody proposed. This restores the filed name and shape; which taxonomy version to reference is left to the working group. Refs IABTechLab#25
IABTechLab#14 doesn't say how deltas combine when a buyer selects more than one priced market, and neither did any comment on it. The earlier commit said the highest delta applies; that is a design choice nobody proposed, so it moves to the PR description as a question. Refs IABTechLab#14
Read the media condition from the ad served when an impression accepts several media, name the seller as the party that serves, place linear TV outside the table, and state environments as (distribution channel, device type) pairs. Insert environments.available[] into the summary list without rewrapping it, shorten the changelog entry and drop audio_streaming from OQ-7 in docs/open-questions.md. Refs IABTechLab#90
time_zone is the line item's clock for daypart windows and
frequency_cap.period: an IANA time zone name or viewer_local. A window
is { days[], start, end } in civil time with an exclusive end later
than start, up to 24:00; a window that crosses midnight is two windows.
List the `days[]` values once in the `daypart` row, as `monday` | ... | `sunday`. Write HH:MM, America/New_York, and UTC as plain text, since they're a format and examples, not field names. The meaning is unchanged.
"At least one of" an empty list is never satisfied, so the bonus line would fail a literal check. An absent key sets no condition. Only the examples change; the any_of[] row and the rules stay as filed in IABTechLab#23. Refs IABTechLab#23
The floor row now says it "neither constrains nor describes the downstream auction floor", as IABTechLab#16 asked, and names that floor: the per-impression floor the exchange applies at auction time (OpenRTB 2.6 Imp.bidfloor). Refs IABTechLab#16
From the Postindustria review on IABTechLab#73: - a replay matches on the same body, every value the buyer sent except idempotency_key, and returns the stored result whether an acceptance or a rejection; a retry against a new version uses a new key; - version increments on each revision of the proposal, not only while published, so a counter under review also moves it; - "deterministic" now says what the checks depend on: the stored proposal and the time the seller receives the attempt; - the price check names the pricing[] entry the commitment is priced against. Refs IABTechLab#24
Give each repeated concept one type in section 2.2, and state the table, presence, type, key, and value rules in section 2.6. Add Type and Required columns to every field and key table. Add the lifecycle, recorded, asserted, and mixed markers, and a line_items[] field. Define the Summary as a strict path subset, with manifest key tables. Delete catalog_ref. Rename the time, reference, delta, cap, and path fields to use the shared types. Number every heading, and keep issue citations and edit history out of normative text. Update the examples, the manifest, the appendices, and the docs. Teach check_appendix.py the new columns and a closed layout list, and add check_examples.py, which checks the examples against the spec's tables.
Make seller a Party in section 4.1, with a required legal_name, and add
buyer and commitment.advertisers[]. Move the registry_id rules to Party
in section 2.2, and scope proposal_id to seller.domain.
Add section 1.2 with the three identity layers. In section 5.8, require
visibility, define each value, and separate who may read from who may
commit. Eligibility is a resolve step that returns not_eligible, and an
unreadable line item returns not_found. A required credential is
{ credential_type, issuer }, and eligible_bidders[] splits into
eligible_buyers[] and eligible_seats[].
Give buyer_requirements, billing, and agreement key tables, and convert
the rows the terms and agreement change added. In section 2.3, drop the
manifest seller_id and filter on status, and ask in section 8 whether a
manifest needs both validators. Update the examples, appendices, and
checkers.
# Conflicts: # examples/proposal.yaml # spec/openproposal-3.0.md
Separate shared and issued proposals, and add issued_from. Define the binding record, record_digest, and assent[] entries, freeze a version's offered terms, and state one staleness rule for asks and holds. Give each act one history action, name the status types, make the line-item transitions exhaustive, define a firm entry per pricing entry, and list the allowed decline grounds once. State agreed once. Remove buyer state from the shared examples, add an issued, partly agreed example, and recompute its record_digest in the checker.
Add selected[] as the complete set of options in effect and value as the one buyer key on a settable field, with an explicit default. An envelope's value narrows allowed[], and the bare-value fallbacks go. Every object option has an id, unique in its field. Name BuyerWrites, LineItemWrites, and the availability read. Give agreed_rate one formula, on the committed entry only, rounded once. Rename gross_rate to list_rate, floor to min_rate, a hint, and delta_pct to delta_fraction, a fraction. Make list_rate and commitment_bounds requestable, mark commitment mixed, and give transaction_mechanism max_select 1. Split geo into a settable geo and a selectable markets over GeoCode, type device as AdCOM codes and daypart as ClockWindow, and delete supported_formats. Update the examples, the issued record_digest, the appendices, and the example checker.
Keep record_digest only on assent entries, and state the exact digest bytes. Cut the record to terms: min_rate, the advisory fields, segments, and the suppression list leave it, and seller_rate and deal_id_issuance go. Give AssentEntry a key table with four roles, so only assent[] decides agreed, and move idempotency_key onto the entry. Name the Commit and its CommitResult. The seller recomputes the digest over the line items as they stand, and rejects in a stated order. A commitment entry stops being live when its commit goes stale or void. Carry entries in messages signed under the OpenProposal profile of Web Bot Auth, as optional evidence. Add HttpsUrl, and replace terms_ref with terms_refs[] of DocumentRef. Add the agreed record example, and teach the checker the assent rules.
Split what a request or response carries from the message envelope in section 1.2, and resolve its placeholders. The wire contract governs only once it is on a standards track. Name the baseline as the VersionRef of the agreed line item at the version of its agreed record. Add change_requests[] in section 4.4 with the OpenDirect 2.1 ChangeRequest fields except webhook, plus baseline, actor, at, and cancellation. Require orderid only when the line item has an order reference. Remove correlation_id, give ExternalReference a key table, rename role to reference_type, and replace io with order and line. Let an entry carry ext outside the record, and ask in section 8 how an entry states a change other than a cancellation. CancellationNotice stays pending until its key table lands. Retitle sections 5.10 and 6.8, and update the agreed example, the appendices, the issue templates, and the checkers. Refs IABTechLab#59, IABTechLab#68
Put one table of transaction_mechanism values in section 5.9. It states
which are deal mechanisms and which delivery_type each allows. Key
section 1.1 on those values, with each one's reference type and change
path. An option is { id, delivery_type }, and sov_basis and sov_share
describe a share of voice. Remove open_auction, because with no deal ID
no bid request can name a line item.
Map a line item agreed under a deal mechanism to a Deals API 1.0 Deal
that deal_origin pushes, with price_type saying how agreed_rate clears.
Map a direct_io line item, and a programmatic guaranteed reservation, to
an OpenDirect 2.1 Order and Line, and state what the record doesn't hold.
Define each creative_delivery.method as a creative path. Remove
buying_route and supported_order_systems, and type serving_platform as a
Domain. Update the examples, digests, manifest, appendices, and checkers.
Refs IABTechLab#132, IABTechLab#133, IABTechLab#93, IABTechLab#109
|
+1 on this handoff, and thanks for picking up the thread from #124. On open question 4: yes, the push should carry the agreed baseline Carrying Happy to help with the wording if that's useful. |
What this changes
Draft 1's § 1.1 names a downstream standard for each mechanism but maps no field. This PR states how an agreed line item reaches OpenDirect 2.1 and the Deals API 1.0.
transaction_mechanismvalues. It marks the deal mechanisms and thedelivery_typeeach allows.{ id, delivery_type }. A share of voice carriessov_basisandsov_share.open_auctionis removed: with no deal ID, no bid request can name a line item.reference_typeand change path.Dealthat the newdeal_originpushes. It maps aCPMentry only. The seller records the deal ID as adealentry (§ 5.10).pricing[].price_typesays howagreed_rateclears. Deal price (§ 5.6) says whatagreed_ratemeans for a deal.direct_ioline item, and aprogrammatic_guaranteedreservation, to anOrderand aLine. It lists what the record doesn't hold.creative_delivery.methodas a creative path.buying_routeandsupported_order_systems[]are removed, andserving_platformis aDomain.Related issue
Closes #132. Closes #133. Both are Postindustria (IAB Tech Lab member) change requests.
Refs #93. This PR carries its
delivery_typebase, and our settlement PR closes it.Refs #109.
Builds on
This PR builds on our post-agreement PR. That PR builds on our signing PR, which builds on our buyer writes PR, our lifecycle PR, our parties PR, our errors and extensions PR, and our shared types PR. Through them, it carries #18, #70, #71, and #72 by @Sirajmx, #73, #74, #75, and #76 by @Kishore-2572, and #118 by @aleksUIX. Review the last commit; earlier ones belong to the PRs it builds on.
Type
Checklist
docs/open-questions.mdOQ-2pricing[].price_type,sov_basis,sov_share, anddeal_originadded;buying_routeandsupported_order_systems[]removed;transaction_mechanismandserving_platformretyped.scripts/check_appendix.pypassesseller-set36 → 38; 83 → 85 fieldsexamples/is updated and still parses — every line item uses the option form. Deal line items gainprice_typeanddeal_origin, and the CTV bonus line item sells asdirect_io. Bothrecord_digestvalues are recomputed, andmanifest.jsonis regenerated. The issued proposal gainscreative_delivery,buyer_requirements, and itsorder,line, anddealreferences.scripts/check_examples.pychecks each option against the § 5.9 table,deal_originand itsdealentry, and a forecastavailabilityundernon_guaranteed, and passesdelivery_type, anddeal_originsit on the line itemselectabledeclaresavailable[]andincluded[]; anythingsettabledeclares bounds (§ 3.1, § 3.2) —transaction_mechanismkeepsavailable[],included[], andmax_select: 1Knock-on effects
serving_platformno longer conforms. Nor does a deal line item withoutdeal_originandprice_type.pricing[].transaction_mechanisms[], as [Change] § 5.6, § 1.1, § 2.1 — Say which price a deal carries: agreed_rate as the Deals API dealfloor, with its pricetype; rename floor to min_rate #133 question 1 asks, an entry prices every mechanism its line item offers.terms.totalcostandLine.costkeep [Change] § 5.6, § 1.1, § 2.1 — Say which price a deal carries: agreed_rate as the Deals API dealfloor, with its pricetype; rename floor to min_rate #133's filed formula. Our pricing PR points them at the seller total (§ 5.6.5), and mapsCPMVand dailyTIMEto aratetype.loop_timeshare counts come with our DOOH PR.external_references[]. Thedealentry carries it, withsystemset todeal_origin.sov_basisand the daypart share follow @premesh's comments on [Comment] § 5.2, § 5.6 — Support share-of-voice and digital screen loop constraints #79, on the two bases and on dayparts.delivery_typemoves onto each option, so each mechanism states its own.pricetypeis spelledprice_type.Open questions
Open, for #132 and #133:
CPMentry? The handoff maps no other cost method ([Change] § 5.6, § 1.1, § 2.1 — Say which price a deal carries: agreed_rate as the Deals API dealfloor, with its pricetype; rename floor to min_rate #133 question 2).programmatic_guaranteedrequire Fixed Price (3)? ([Change] § 5.6, § 1.1, § 2.1 — Say which price a deal carries: agreed_rate as the Deals API dealfloor, with its pricetype; rename floor to min_rate #133 question 3)deal_origineach, or a selectable one ([Change] § 1.1, § 5.9, § 5.10 — Carry the Deals API deal: its id, its origin, and the buyer's seats #132 question 2)?VersionRef, so a DSP can join a deal to its record ([Change] § 1.1, § 5.9, § 5.10 — Carry the Deals API deal: its id, its origin, and the buyer's seats #132 question 3)?By opening this pull request I agree my contribution may be published under this
repository's licence, as set out in CONTRIBUTING.md.
Analyzed and generated with the help of Claude, fully reviewed by me.