Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,35 @@ major version bump.

## [Unreleased]

### Changed

- **Breaking:** name-length bounds conformed to the OpenDirect 2.1 spec
(the IAB direct-buying API standard): `Product.name` max length 128 ->
100; `Organization.name` and `Account.name` max length 128 -> 120.
Bounds already matching spec (`Creative.name` 255, the lifecycle `name`
fields at 200) are unchanged. Breaking for any integrator currently
sending names in the 101-128 (Product) or 121-128 (Organization/
Account) range.

### Added

- `NegotiationMessage.agent_url` (optional): the sender's registered A2A
agent URL, for registry trust-tier verification. Optional for backward
compatibility with callers that predate this field.
- `Deal.curation` (optional): an IAB Deals API v1.0 `Curation` object
(`deal-api` spec, `deal1.0.md`) — `curator` (canonical domain of the
packaging entity), `curator_deal_id` (the curator's own deal id, spec
`cdealid`), `curation_fee_type` (spec `curfeetype`; 0=undisclosed,
1=percent of spend, 2=flat fee, 3=CPM, 4=no fee), and an `ext` slot.
Curated packages (Index Inventory/Auction Packages, PubMatic Auction
Packages, Magnite Curate, and the like) are represented by this object
riding alongside any `DealType`, not by a new `DealType` value:
pricing mechanic and packaging structure are orthogonal per the
published spec. Two candidate `DealType` wire values, `CUR` and the
industry term "PMP", were considered and rejected in favor of this
spec-aligned object; see the `DealType` docstring for the full
disambiguation.

## [0.5.1] - 2026-08-10

First tagged release published on GitHub.
Expand Down
2 changes: 1 addition & 1 deletion STANDARDS_GAP_REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ What is **not** verified is external-spec *fidelity*. The contract borrows names

- **Status:** UNVERIFIED
- **Registry id:** `iab-deals-api-1.0`
- **Unverified / missing:** No vectors derived from the published Deals API v1.0 examples/schemas. Internal vectors cover our reconciled quote->book envelopes only.
- **Unverified / missing:** No vectors derived from the published Deals API v1.0 examples/schemas. Internal vectors cover our reconciled quote->book envelopes only. The Curation object (deal-api spec, deal1.0.md) is now modeled as Deal.curation, but against this repo's own transcription of the field table, not against a hash-pinned copy of the publication or its example payloads.
- **To close this gap:** Obtain the published IAB Deals API v1.0 schemas and example payloads and add vectors derived from them; assert our reconciled quote->book envelopes validate against the published request/response shapes.

#### IAB Tech Lab AAMP agent discovery and trust registry
Expand Down
6 changes: 6 additions & 0 deletions spec/fixtures/primitives/Deal.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"verified_at": "2026-07-01T12:00:00Z"
},
"created_at": "2026-07-01T12:00:00Z",
"curation": null,
"deal_id": "deal-001",
"deal_type": "PG",
"expires_at": null,
Expand Down Expand Up @@ -107,6 +108,7 @@
"buyer_tier": "agency",
"consent_context": null,
"created_at": "2026-07-01T12:00:00Z",
"curation": null,
"deal_id": "deal-ltv-002",
"deal_type": "PG",
"expires_at": null,
Expand Down Expand Up @@ -190,6 +192,7 @@
"buyer_tier": "public",
"consent_context": null,
"created_at": "2026-07-02T09:30:00Z",
"curation": null,
"deal_id": "deal-003",
"deal_type": "PD",
"expires_at": null,
Expand Down Expand Up @@ -257,6 +260,7 @@
"verified_at": "2026-07-01T12:00:00Z"
},
"created_at": "2026-07-01T12:00:00Z",
"curation": null,
"deal_id": "deal-001",
"deal_type": "PG",
"expires_at": null,
Expand Down Expand Up @@ -356,6 +360,7 @@
"verified_at": "2026-07-01T12:00:00Z"
},
"created_at": "2026-07-01T12:00:00Z",
"curation": null,
"deal_id": "deal-001",
"deal_type": "PG",
"expires_at": null,
Expand Down Expand Up @@ -453,6 +458,7 @@
"verified_at": "2026-07-01T12:00:00Z"
},
"created_at": "2026-07-01T12:00:00Z",
"curation": null,
"deal_id": "deal-001",
"deal_type": "PG",
"expires_at": null,
Expand Down
2 changes: 2 additions & 0 deletions spec/fixtures/protocol/DealBookingResponse.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"verified_at": "2026-07-01T12:00:00Z"
},
"created_at": "2026-07-01T12:00:00Z",
"curation": null,
"deal_id": "deal-001",
"deal_type": "PG",
"expires_at": null,
Expand Down Expand Up @@ -141,6 +142,7 @@
"verified_at": "2026-07-01T12:00:00Z"
},
"created_at": "2026-07-01T12:00:00Z",
"curation": null,
"deal_id": "deal-001",
"deal_type": "PG",
"expires_at": null,
Expand Down
5 changes: 5 additions & 0 deletions spec/fixtures/protocol/NegotiationMessage.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
{
"data": {
"action": "counter",
"agent_url": null,
"buyer_identity": {
"advertiser_id": null,
"advertiser_industry": null,
Expand Down Expand Up @@ -37,6 +38,7 @@
{
"data": {
"action": "accept",
"agent_url": null,
"buyer_identity": null,
"buyer_price": {
"amount_micros": 22000000,
Expand All @@ -56,6 +58,7 @@
{
"data": {
"action": "reject",
"agent_url": null,
"buyer_identity": null,
"buyer_price": null,
"idempotency_key": "00000000-0000-4000-8000-000000000012",
Expand All @@ -72,6 +75,7 @@
{
"data": {
"action": "counter",
"agent_url": null,
"buyer_identity": {
"advertiser_id": null,
"advertiser_industry": null,
Expand Down Expand Up @@ -104,6 +108,7 @@
"description": "FD-13: unknown + x_ fields on a negotiation message are ignored",
"expected": {
"action": "counter",
"agent_url": null,
"buyer_identity": {
"advertiser_id": null,
"advertiser_industry": null,
Expand Down
3 changes: 2 additions & 1 deletion spec/jsonschema/Account.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,15 @@
"name": {
"anyOf": [
{
"maxLength": 128,
"maxLength": 120,
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "OpenDirect 2.1 identity name bound (120).",
"title": "Name"
},
"seller_organization_id": {
Expand Down
85 changes: 84 additions & 1 deletion spec/jsonschema/Deal.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,77 @@
"title": "ConsentContext",
"type": "object"
},
"Curation": {
"description": "IAB Deals API v1.0 ``Curation`` object (``deal-api`` spec,\n``deal1.0.md``): identifies the entity that packaged inventory,\ntechnology, and/or data into a deal.\n\nOrthogonal to :class:`DealType` (see its docstring) \u2014 a curated deal\nstill has a pricing mechanic (``PG``/``PD``/``PA``); this object\nlayers packaging provenance on top and can accompany any of them.\nEvery field is optional per the published spec, matching the\nspec's own all-optional ``Curation`` object.",
"properties": {
"curation_fee_type": {
"anyOf": [
{
"$ref": "#/$defs/CurationFeeType"
},
{
"type": "null"
}
],
"default": null,
"description": "Fee-structure classification for curation services (spec ``curfeetype``); see :class:`CurationFeeType`."
},
"curator": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Canonical domain of the business entity that did the packaging of inventory, technology and/or data (spec ``curator``).",
"title": "Curator"
},
"curator_deal_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Deal id in the curator's own namespace (spec ``cdealid``).",
"title": "Curator Deal Id"
},
"ext": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Extension slot (spec placeholder for deal-specific ext).",
"title": "Ext"
}
},
"title": "Curation",
"type": "object"
},
"CurationFeeType": {
"description": "Deals API v1.0 ``Curation.curfeetype``: how the curator's fee is\nstructured. Communicates fee STRUCTURE only, never the fee amount\nitself (per the published spec).\n\n- ``0`` = undisclosed\n- ``1`` = percentage of spend\n- ``2`` = flat fee\n- ``3`` = CPM (cost per mille)\n- ``4`` = no fee",
"enum": [
0,
1,
2,
3,
4
],
"title": "CurationFeeType",
"type": "integer"
},
"DealStatus": {
"description": "ONE deal status vocabulary, unioned from the four competing sets.\n\nSources: buyer ``DealResponse.status`` (proposed/active/rejected/\nexpired/completed), seller ``DealBookingStatus`` (proposed/active/\nexpired/cancelled), buyer ``BuyerDealStatus`` (quoted/negotiating/\naccepted/booking/booked/delivering/... + linear TV extensions).\n\nRecorded aliases (retired values, NOT valid on the wire):\n``delivering`` -> ``active``; ``booking`` -> ``booked``;\n``quoted`` -> represented by QuoteStatus, not a deal state;\n``partially_canceled`` -> ``partially_cancelled``.",
"enum": [
Expand All @@ -168,7 +239,7 @@
"type": "string"
},
"DealType": {
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers.\n This is the general private-marketplace tier \u2014 bare \"PMP\" in\n industry usage most often means this.\n\nCurated packages are NOT a fourth deal type. A curated deal (Index\nExchange Inventory/Auction Packages, PubMatic Auction Packages,\nMagnite Curate, and the like) is represented by the optional\n:class:`Curation` object riding alongside a deal, not by a distinct\n``DealType`` value \u2014 pricing mechanic and packaging structure are\northogonal, and the ``Curation`` object can accompany any\n``DealType`` (curated deals are typically ``PA``-mechanics \u2014 floor +\ncompetitive bid \u2014 but a curated deal can equally be ``PG``). Two\nwire-value candidates for a fourth deal type, ``CUR`` and the\nindustry term \"PMP\", were considered and rejected during design: both\nwould have collapsed a packaging/provenance concept into the\npricing-mechanic enum, which the published IAB Deals API v1.0\n``Curation`` object (``deal-api`` spec, ``deal1.0.md``) deliberately\nkeeps separate. See :class:`Curation` for the wire shape; seller-\ninternal \"PMP curated deals\" terminology maps to a deal carrying a\npopulated ``Curation`` object, not to a dedicated ``DealType`` value.\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"enum": [
"PG",
"PD",
Expand Down Expand Up @@ -738,6 +809,18 @@
"title": "Created At",
"type": "string"
},
"curation": {
"anyOf": [
{
"$ref": "#/$defs/Curation"
},
{
"type": "null"
}
],
"default": null,
"description": "IAB Deals API v1.0 Curation object, present when this deal is a curated package (see the DealType docstring: curation is orthogonal to deal_type, not a value of it)."
},
"deal_id": {
"description": "Seller-issued deal identifier.",
"title": "Deal Id",
Expand Down
3 changes: 2 additions & 1 deletion spec/jsonschema/Organization.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@
"title": "Ext"
},
"name": {
"maxLength": 128,
"description": "OpenDirect 2.1 identity name bound (120).",
"maxLength": 120,
"title": "Name",
"type": "string"
},
Expand Down
5 changes: 3 additions & 2 deletions spec/jsonschema/Product.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"type": "object"
},
"DealType": {
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers.\n This is the general private-marketplace tier \u2014 bare \"PMP\" in\n industry usage most often means this.\n\nCurated packages are NOT a fourth deal type. A curated deal (Index\nExchange Inventory/Auction Packages, PubMatic Auction Packages,\nMagnite Curate, and the like) is represented by the optional\n:class:`Curation` object riding alongside a deal, not by a distinct\n``DealType`` value \u2014 pricing mechanic and packaging structure are\northogonal, and the ``Curation`` object can accompany any\n``DealType`` (curated deals are typically ``PA``-mechanics \u2014 floor +\ncompetitive bid \u2014 but a curated deal can equally be ``PG``). Two\nwire-value candidates for a fourth deal type, ``CUR`` and the\nindustry term \"PMP\", were considered and rejected during design: both\nwould have collapsed a packaging/provenance concept into the\npricing-mechanic enum, which the published IAB Deals API v1.0\n``Curation`` object (``deal-api`` spec, ``deal1.0.md``) deliberately\nkeeps separate. See :class:`Curation` for the wire shape; seller-\ninternal \"PMP curated deals\" terminology maps to a deal carrying a\npopulated ``Curation`` object, not to a dedicated ``DealType`` value.\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"enum": [
"PG",
"PD",
Expand Down Expand Up @@ -258,7 +258,8 @@
"title": "Ext"
},
"name": {
"maxLength": 128,
"description": "OpenDirect 2.1 Product name bound (100).",
"maxLength": 100,
"title": "Name",
"type": "string"
},
Expand Down
2 changes: 1 addition & 1 deletion spec/jsonschema/Proposal.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"type": "string"
},
"DealType": {
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers.\n This is the general private-marketplace tier \u2014 bare \"PMP\" in\n industry usage most often means this.\n\nCurated packages are NOT a fourth deal type. A curated deal (Index\nExchange Inventory/Auction Packages, PubMatic Auction Packages,\nMagnite Curate, and the like) is represented by the optional\n:class:`Curation` object riding alongside a deal, not by a distinct\n``DealType`` value \u2014 pricing mechanic and packaging structure are\northogonal, and the ``Curation`` object can accompany any\n``DealType`` (curated deals are typically ``PA``-mechanics \u2014 floor +\ncompetitive bid \u2014 but a curated deal can equally be ``PG``). Two\nwire-value candidates for a fourth deal type, ``CUR`` and the\nindustry term \"PMP\", were considered and rejected during design: both\nwould have collapsed a packaging/provenance concept into the\npricing-mechanic enum, which the published IAB Deals API v1.0\n``Curation`` object (``deal-api`` spec, ``deal1.0.md``) deliberately\nkeeps separate. See :class:`Curation` for the wire shape; seller-\ninternal \"PMP curated deals\" terminology maps to a deal carrying a\npopulated ``Curation`` object, not to a dedicated ``DealType`` value.\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"enum": [
"PG",
"PD",
Expand Down
2 changes: 1 addition & 1 deletion spec/jsonschema/Quote.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@
"type": "object"
},
"DealType": {
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"description": "Programmatic deal types. The short wire encoding is canonical.\n\n- ``PG`` = Programmatic Guaranteed: fixed price, guaranteed impressions\n- ``PD`` = Preferred Deal: fixed price, non-guaranteed first look\n- ``PA`` = Private Auction: auction with floor price, invited buyers.\n This is the general private-marketplace tier \u2014 bare \"PMP\" in\n industry usage most often means this.\n\nCurated packages are NOT a fourth deal type. A curated deal (Index\nExchange Inventory/Auction Packages, PubMatic Auction Packages,\nMagnite Curate, and the like) is represented by the optional\n:class:`Curation` object riding alongside a deal, not by a distinct\n``DealType`` value \u2014 pricing mechanic and packaging structure are\northogonal, and the ``Curation`` object can accompany any\n``DealType`` (curated deals are typically ``PA``-mechanics \u2014 floor +\ncompetitive bid \u2014 but a curated deal can equally be ``PG``). Two\nwire-value candidates for a fourth deal type, ``CUR`` and the\nindustry term \"PMP\", were considered and rejected during design: both\nwould have collapsed a packaging/provenance concept into the\npricing-mechanic enum, which the published IAB Deals API v1.0\n``Curation`` object (``deal-api`` spec, ``deal1.0.md``) deliberately\nkeeps separate. See :class:`Curation` for the wire shape; seller-\ninternal \"PMP curated deals\" terminology maps to a deal carrying a\npopulated ``Curation`` object, not to a dedicated ``DealType`` value.\n\nMapping from the seller repo's retired long-form encoding\n(``models/core.py``): ``programmaticguaranteed`` -> ``PG``,\n``preferreddeal`` -> ``PD``, ``privateauction`` -> ``PA``. The\nlong-form strings are NOT valid wire values.",
"enum": [
"PG",
"PD",
Expand Down
Loading
Loading