Skip to content

spec(media-buy): billing authority + finality flags for 3.1 (#2391 part 1)#4735

Merged
bokelley merged 2 commits into
mainfrom
bokelley/issue-2391
May 18, 2026
Merged

spec(media-buy): billing authority + finality flags for 3.1 (#2391 part 1)#4735
bokelley merged 2 commits into
mainfrom
bokelley/issue-2391

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Summary

Closes the prerequisite half of #2391 — a buyer reading the 3.1 spec can answer "where do I look for the billing-grade number, and has it stopped moving?" without any new tasks:

  • Existing measurement_terms.billing_measurement.vendor (BrandRef) already names authority — seller's ad server, buyer's 3PAS, or named third-party vendor.
  • New finality flags on both reporting surfaces mark when numbers are closed for invoicing.
  • New finalization_deadline_hours gives sellers a graceful fallback to seller-attested when the authoritative party misses the deadline.

The structured dispute task that builds on this foundation is tracked as #4734 for 3.2 — the data shape needed to dispute (final records with timestamps, attestation, measurement window, makegood menu) is already on the wire after this PR.

Changes

  • get-media-buy-delivery-response.json — row-level is_final + finalized_at on media_buy_deliveries[*]; finalized_at on by_package[*] next to existing is_final.
  • report-usage-request.jsonfinal (default true when absent — preserves existing semantics), finalized_at, measurement_window on each usage record. Symmetric with seller-side. Description acknowledges sales-agent receivers for buyer-attested / vendor-attested reconciliation.
  • measurement-terms.json — optional finalization_deadline_hours on billing_measurement. Missed deadline → seller MAY fall back to seller-attested and handle under makegood_policy.
  • get_media_buy_delivery.mdx — replaced "AdCP 3.0 does not specify a structured dispute task" punt with normative "Final vs provisional" + "Who is authoritative for billing" sections; flags dispute task for 3.2.
  • billing-authority.mdx — new normative advanced-topics page with worked examples (seller-attested default, buyer-3PAS, vendor-attested Nielsen).
  • docs.json — register the new page under media-buy → Concepts.

Backward compatibility

Strictly additive — no field shape changes, no new required fields. Agents that don't emit the new flags remain spec-valid; absent semantics match the 3.0 baseline.

Design conversation

The design walked through several alternatives that landed in their current shape:

  1. Started broader (new dispute task family) → realized buy terms already named authority via measurement_terms.billing_measurement.
  2. Considered "best-effort vs billing-grade" as the framing → simplified to final vs not final per measurement window. "Best-effort" was euphemism; either the period is closed or it isn't.
  3. Considered inventing submit_billing_measurement for routine third-party reconciliation → noticed report_usage already covers this and just needed the same finality flag the delivery side gets.
  4. Considered shipping disputes in 3.1 → deferred to 3.2 (3.2: Structured delivery dispute task (state machine, audit, SLA) #4734). Routine reconciliation is the high-volume path; disputes are the rare unhappy path. Spec the routine path first.

Test plan

  • Build site locally and verify billing-authority.mdx renders + nav placement (docs/media-buy/advanced-topics/billing-authority)
  • Verify all internal links resolve (/docs/media-buy/advanced-topics/accountability, /docs/accounts/tasks/report_usage, /docs/media-buy/task-reference/get_media_buy_delivery, /docs/media-buy/media-buys/optimization-reporting)
  • Confirm https://adcontextprotocol.org/schemas/v3/core/measurement-terms.json link in the page footer
  • Schema validation passes for get-media-buy-delivery-response.json, report-usage-request.json, measurement-terms.json
  • Confirm absent-field semantics: a response with no is_final / final continues to parse and behave as 3.0

🤖 Generated with Claude Code

… 3.1; disputes deferred to #4734)

Closes the prerequisite half of #2391 — a buyer reading 3.1 can answer
"where do I look for the billing-grade number, and has it stopped moving?"
without any new tasks. Existing `measurement_terms.billing_measurement`
already names authority; new finality flags on both reporting surfaces
mark when numbers are closed for invoicing.

- `get-media-buy-delivery-response.json`: row-level `is_final` +
  `finalized_at` on `media_buy_deliveries[*]`; `finalized_at` on
  `by_package[*]` next to existing `is_final`.
- `report-usage-request.json`: `final` (default true), `finalized_at`,
  `measurement_window` on each usage record. Symmetric with seller-side.
  Description acknowledges sales-agent receivers for buyer-attested /
  vendor-attested reconciliation.
- `measurement-terms.json`: optional `finalization_deadline_hours` on
  `billing_measurement`. Miss → seller MAY fall back to seller-attested
  and handle under `makegood_policy`.
- `get_media_buy_delivery.mdx`: replace "3.0 does not specify a dispute
  task" punt with normative "Final vs provisional" + "Who is
  authoritative for billing" sections.
- `billing-authority.mdx`: new normative page with worked examples
  (seller-attested, buyer-3PAS, vendor-attested Nielsen).
- `docs.json`: register the new page.

Strictly additive — no field shape changes, no new required fields.
Agents that don't emit the new flags remain spec-valid; absent semantics
match the 3.0 baseline.

The structured dispute task that builds on this foundation is tracked
as #4734 for 3.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley added this to the 3.1.0 milestone May 18, 2026
…line anchor, consistency MUST

Three expert reviews (protocol, ad-tech product, docs) on the initial
commit surfaced two blockers and several small fixes. Addressing them
in one follow-up commit:

Blockers fixed:
- `report_usage.final`: changed from "default true on absence" to
  "absent means unknown." The original default would have silently
  promoted every daily pacing push into an invoice trigger the moment
  a seller wired up this logic. Now: receivers MUST NOT invoice on
  absent for buys where `billing_measurement` names the reporter as
  authoritative. Non-media-buy variants (signals/governance/creative/
  brand) MAY treat absent as final, preserving 3.0 semantics.
- `finalization_deadline_hours`: replaced the ambiguous
  "reporting_period.end OR window close" anchor with a single rule —
  when `measurement_window` is set, hours count from window close;
  otherwise from `reporting_period.end`. The OR was a multi-day
  divergence on windowed channels. Also documented symmetry — the
  deadline applies to whichever party is named in `vendor`, not just
  the buyer.

Small fixes:
- `is_final` row-level: added normative MUST that sellers MUST NOT emit
  `is_final: true` at the row level unless every package is final for
  the same window; package-level is authoritative on disagreement.
  Tightened description (row doesn't carry its own `measurement_window`).
- `billing-authority.mdx`: BrandRef link to `/docs/brand-protocol/brand-json`
  instead of glossary; struck reference to phantom
  `delivery_reporting.finalization_window_hours` capability (only
  existed in 3.0 doc text, never shipped as a schema field); added
  reality callouts on holdco/operator pushers, two vendor-attested
  patterns (seller-pulled vs buyer-pushed), SSP RTB realistically
  staying seller-attested; added section distinguishing webhook
  `notification_type: "final"` from row `is_final`.
- `get_media_buy_delivery.mdx`: vendor-attested bullet rewritten to
  reflect two operational patterns; symmetric deadline language.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley
Copy link
Copy Markdown
Contributor Author

Expert review + follow-up

Ran three expert reviewers (protocol, ad-tech product, docs) on the initial commit. Two findings were merge-blockers; the rest were small fixes. All addressed in ae3b189:

Blockers fixed:

  1. report_usage.final default (product expert) — Original schema defaulted final to true on absence. Real-world agency 3PAS flow pushes daily pacing data plus a settled month-end file; a true default would have silently promoted every pacing push into an invoice trigger. Changed to "absent = unknown": receivers MUST NOT invoice on absent for buys where measurement_terms.billing_measurement names the reporter as authoritative. Non-media-buy variants (signals/governance/creative/brand — domains with no provisional-state concept) MAY treat absent as final, preserving 3.0 behavior.

  2. finalization_deadline_hours anchor (protocol expert) — Original said "hours after reporting_period.end (or after measurement_window close)." For a C7 buy those anchors are a week apart and the OR was ambiguous. Picked a single rule: when measurement_window is set, hours count from window close; otherwise from reporting_period.end. Also documented symmetry — the deadline applies to whichever party is named in vendor, not just the buyer.

Small fixes:

  • Row-level is_final consistency MUST (protocol) — added normative "Sellers MUST NOT emit is_final: true at the row level unless every package is final for the same window; on disagreement, package-level is authoritative." Tightened description (row doesn't carry its own measurement_window).
  • Phantom capability (protocol)delivery_reporting.finalization_window_hours was referenced in billing-authority.mdx but only exists in 3.0 doc text, never shipped as a schema field. Struck the reference.
  • BrandRef link (docs) — pointed at /docs/brand-protocol/brand-json instead of glossary, matching the convention used by accountability.mdx.
  • Vendor-attested operational reality (product) — Original example assumed orchestrator pushes vendor numbers. Real CTV pattern is seller-pulled (NPower, IAS dashboard) and published on get_media_buy_delivery. Rewrote the example to cover both patterns (seller-pulled when seller holds the vendor relationship; buyer-pushed when buyer holds it).
  • Doer reality for 3PAS (product) — Acknowledged the pusher is typically a holdco operator (Choreograph, Annalect) wrapping CM360/Flashtalking exports, not the agency seat itself.
  • SSP RTB realism (product) — Added explicit note that SSPs running RTB will keep being seller-attested for the foreseeable future; first real adopters are CTV direct sellers whose Nielsen-backed guarantees already invoice this way.
  • Webhook vs row finality (protocol) — Added section clarifying that notification_type: "final" (last webhook of the campaign) is independent of is_final: true (this row is closed for invoicing).

Deferred:

  • Capability-side finalization signaling (delivery_reporting.finalization_window_hours or equivalent) — file as 3.2 follow-up if needed; buy-term finalization_deadline_hours is sufficient for the contract today.
  • Seller-initiated deadline_breached opener — folded into 3.2 3.2: Structured delivery dispute task (state machine, audit, SLA) #4734's dispute task scope.

🤖 Generated with Claude Code

@bokelley bokelley merged commit a8aa0ab into main May 18, 2026
18 checks passed
@bokelley bokelley deleted the bokelley/issue-2391 branch May 18, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant