Repository navigation
§ 5.6.2 — Holds: offered hold terms and the live hold (#66) - #143
KonstantinMirin wants to merge 69 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.
State that held means reserved inventory. Keep a hold live while a commit made during it is open, so a seller can't leave the commit unanswered until the hold lapses and then decline it for lack of availability. Add the seller's hold reference and renewal_right, an offered term for the right to the next cycle. Refs IABTechLab#66
|
Thanks, @premesh and @EvgenyAndroid. All four are in this PR, so you can review them here:
Analyzed and generated with the help of Claude, fully reviewed by me. |
The manifest's hold_limits states the most holds a seller grants at once, per buyer domain and per advertiser. A seller declines a request over a declared limit, and under a per-advertiser limit may decline one that names no advertiser.
|
Hold terms by transaction mechanism Evidence from a desk test against this stack at In this PR, Two questions:
|
|
Thanks, @jaanijuk. Yes, two line items is the intended form (§ 3.1). A price varies by route because each route has its own price. A hold or an exclusivity term is a promise about the inventory itself. Your first question has an answer in § 5.6.2: holds on the same inventory reserve it in grant order ( So I'd rather not add Analyzed and generated with the help of Claude, fully reviewed by me. |
Add the rule @aleksUIX proposed on IABTechLab#42: a stitched preview uses a session with no production notifications, or the creative is inspected outside the stitched stream.
What this changes
Draft 1 keeps a hold's offer and its live state in one object,
hold_status, so § 4.1.1 needs a removal path with an exception. This PR splits the object in two.hold_terms,{ available, duration }, offers the hold. It isseller-setand inside the record. An absenthold_termsoffers no hold, and asking for one returnsnot_offered(§ 2.4).holdis the buyer's hold. It islifecycle, outside the record, and absent until the buyer first asks. It carriesstatus,version,position, andvalid_until.holdkeys state terms:requested_terms, aLineItemWrites, andheld_availability, what the seller reserves.HoldStatusaddsdeclinedand dropsnone. The transitions are exhaustive. A declined, expired, or released hold can be asked again, and a re-offer is the listedheld→heldtransition.hold.version.hold.referenceis the seller's id for the reservation, andrenewal_rightis an offered term for the next cycle.hold_scopeis removed. Underlinked_execution: all, the holds on a proposal move as a unit. Our packages PR moves this rule to packages.hold_limitscaps concurrent holds per buyer and per advertiser, from @pkanna000 and @premesh.Related issue
Closes #66, a change request from Postindustria (IAB Tech Lab member).
Refs #67 and #156.
Builds on
This PR builds on our buyer writes PR. That PR builds on 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 commits "Split holds into offered terms and live state", "Answer the open hold questions and offer a renewal right", "Let a seller declare hold limits in the manifest", and "Put the holds entry's Breaking marker at its end"; the others belong to those PRs.
Type
Checklist
hold_statusrow becomeshold_terms(seller-set) andhold(lifecycle).scripts/check_appendix.pypasses, withAvailabilitylisted as a pending typeseller-set38 → 40,lifecycle1 → 2,mixed6 → 5; 87 → 89 fieldsexamples/is updated and still parses —line-item-ctv.yamloffers no hold on itsprivate_auctionline item.line-item-display.yamlandproposal-issued.yamlcarryhold_terms.proposal-issued.yamlshows a hold granted on version 1 and re-offered at version 2, and itsrecord_digestis recomputed.scripts/check_examples.pypasses, and checksrequested_termsagainst theBuyerWritespathshold_termsandholdsit on the line itemselectabledeclaresavailable[]andincluded[]; anythingsettabledeclares bounds (§ 3.1, § 3.2) — no newselectableorsettablefieldsKnock-on effects
hold_statusis removed.hold_availableandhold_durationbecomehold_terms.availableandduration.stateandhold_expires_atbecomehold.statusandvalid_until. A line item that carriedhold_statusgets a newrecord_digest.held_availabilityrow in § 5.6.2 has the TypeAvailability, which has no key table yet. It stays a pending type until our pricing PR adds that table. Until then, the example uses theavailabilityrow's keys.declinedis the value [Change] § 5.6.2 — hold_status needs refused and re-entry to requested #66 asked for asrefused. Our comment on #66 asked that a hold freeze the held terms. A seller change is a new version, so the seller re-offers or releases the hold.positionis the queue order @premesh describes on #66: seller-set, in the order granted. It answers [Change] § 5.6.2 — hold_status needs refused and re-entry to requested #66 question 5.held_availabilitycarries the held quantity @EvgenyAndroid asks for, answering [Change] § 5.6.2 — hold_status needs refused and re-entry to requested #66 question 7. Their point that the queue position is status, which @premesh agrees with, puts all ofholdoutside the record.available: falsewhere no inventory is reserved, as @EvgenyAndroid and @premesh agree.hold.positionin the exception to its bar on declining.Open questions
Answered in review:
commitmade while it runs.held→expiredwaits until thatcommitis answered. Otherwise a seller could leave it unanswered until the hold lapses, then decline for lack of availability.heldmeans reserved inventory, as @EvgenyAndroid and @premesh agree. A seller that only honors terms useshold_terms.available: falsewithprice_valid_until.hold.referenceholds the seller's id, as @EvgenyAndroid asks ([Change] § 5.6.2 — hold_status needs refused and re-entry to requested #66 question 6).holdis live state, so it adds no version.renewal_rightis @premesh's first refusal, negotiated, so an offered term inside the record, as @EvgenyAndroid agrees.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.