Skip to content

§ 5.3, § 5.5 — Audience: composition, disclosure, and one map of exclusions (#91, #97) - #151

Open
KonstantinMirin wants to merge 273 commits into
IABTechLab:mainfrom
KonstantinMirin:op30-audience
Open

KonstantinMirin wants to merge 273 commits into
IABTechLab:mainfrom
KonstantinMirin:op30-audience

Conversation

@KonstantinMirin

@KonstantinMirin KonstantinMirin commented Nov 4, 2026 •

Copy link
Copy Markdown

What this changes

Draft 1 doesn't say whether two selected audiences combine as AND or OR, or what an audience option discloses. Its block lists are an untyped object. This PR gives § 5.3 and § 5.5 key tables.

  • § 5.3: combine is a Selectable of any and all. The seller's included[] names the operator in effect, and the buyer's selected[] changes it.
  • § 5.3: a buyer can exclude options the seller marks excludable, in excluded[], and their deltas count. Four numbered conditions state whom the line item targets.
  • § 5.3: each AudienceOption states its taxonomy, its Data Transparency Standard 1.1 data_label, and its sensitive_categories[].
  • § 5.3: addressable_scale is an AddressableScale, with a unit and a window.
  • § 5.3: BuyerSupplied and BuyerList have key tables. expected_match_rate is a Fraction, and changes without a new version (§ 4.1.3).
  • § 5.5: accepted_exclusions maps each exclusion type to its enforcement and max_entries. exclusions is one map keyed by type. A long list is a DocumentRef (§ 2.2). An exclusion added after agreed is a delivery.
  • § 5.5: the disclosure rule moves here from § 5.8. It admits the systems and vendors that enforce or verify the lists.
  • § 6.3: the CTV example accepts keywords only as best_effort.

Related issue

Closes #91. Closes #97. Both are Postindustria (IAB Tech Lab member) change requests.

Refs #39, #25, and #71.

Builds on

This PR builds on our property option PR, which builds on our DOOH PR. It builds on our settlement PR, pricing PR, handoff PR, post-agreement PR, and holds PR. These build on our signing PR, buyer writes PR, lifecycle PR, parties PR, errors and extensions PR, and shared types PR. Through them it carries #17, #18, #70, #71, and #72 by @Sirajmx, #73, #74, #75, and #76 by @Kishore-2572, and #117 and #118 by @aleksUIX. Review the commit "Define audience composition and one map of exclusions".

Type

  • Editorial — no normative effect
  • Normative — changes what the spec requires
  • Breaking — would break an implementation already built to this draft
  • Examples / schema / docs only

Checklist

  • The body section is updated — § 2.4, § 2.6, § 2.7, § 3, § 3.4, § 4.1.1, § 4.1.3, § 5.3, § 5.5, § 5.6, § 5.6.1, § 5.8, and § 6.3
  • Appendix A (field index) matches the body — the audiences, addressable_scale, accepted_exclusions, and exclusions rows take the new types. scripts/check_appendix.py passes
  • Appendix B (fields by marker, and the counts) matches the body — exclusions_accepted becomes accepted_exclusions; no count changes
  • Any affected example in examples/ is updated and still parses. The display example writes combine, data_label, expected_match_rate, seller_side, and the accepted_exclusions map. The issued example writes the map, with keywords by reference, and recomputes record_digest. The CTV example gains accepted_exclusions. scripts/check_examples.py checks combine, excluded[], and Map keys, and passes. scripts/check_pricing_fixtures.py passes 60 of 60
  • RFC 2119 keywords used deliberately — MUST / SHOULD / MAY are not synonyms
  • The no-inheritance rule (§ 2) still holds — every key sits on the line item
  • Anything selectable declares available[] and included[]; anything settable declares bounds (§ 3.1, § 3.2) — combine declares both; no new settable fields
  • No confidential or commercially sensitive content

Knock-on effects

Open questions

  1. [Change] § 5.3 — Say whether selected audiences combine as AND or OR, and how buyer segments and exclusions combine with them #91's questions 2, 3, and 5 carry over; question 4 is answered there. Question 1 is settled: combine is REQUIRED when more than one seller audience can be in effect, and included[] holds the seller's default.
  2. max_entries has no stated consequence. Should a seller reject a list over the limit, as our comment on #71 suggests?
  3. What does pre_serve promise? [Change] § 5.5 — Buyer-supplied block lists (exclusions_accepted, exclusions) #25 defers guaranteed enforcement to 4.0, and this PR doesn't define it.
  4. [Change] § 5.2, § 5.3 — Disclose audience taxonomy, data label, sensitive categories, and child-directed properties #97's questions 2, 3, and 5 carry over. Question 4 is settled: sensitive_categories[] is an open vocabulary with ten core terms.

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.

KonstantinMirin and others added 30 commits October 25, 2026 14:47
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
- Rename apps[].domain to developer_domain, as IABTechLab#15 filed it. It is the
  developer domain from the store listing that app-ads.txt uses, not
  OpenRTB 2.6 app.domain, which is the domain of the app.
- Define platform as the store that issued bundle, since a bundle is
  unique only within its store.
- List apps[].platform and apps[].bundle in the Summary Representation
  (section 2.1), as IABTechLab#15 asked.

Refs IABTechLab#15
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.
Add section 2.4: a rejection returns errors[], one or more ErrorObjects
with an open code list, retry values that read for any request, and
informative HTTP fits. Name the code each rejection in sections 5.5,
5.6.1, and 5.6.3 returns, and drop the requires cycle ban.

Add the Ext type and section 2.5: ext on the proposal and each line
item, keyed by owner domain, ignored and kept when not understood, and
frozen at agreed. Make spec_version a required proposal field, and ask
in section 8 how a receiver reads a later release.

Add informative security considerations in section 2.7. Word the open
questions without a count. Update the examples and appendices, and
teach check_examples.py the Ext type and the open ErrorObject.
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.
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.
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.
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants