From d661f95cf468a237f2367c0b0e187522bda6eba0 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Tue, 19 May 2026 10:09:27 -0700 Subject: [PATCH] docs(reference): re-apply whats-new-in-3-1.mdx (post-revert restore) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-applies the 3.0 → 3.1 announcement page on a fresh branch from current main. The original landing (PR #4784, commit e01cbf907b) accidentally deleted 1,131 dist/docs/ versioned-snapshot files because its branch tree-state was based on a stale local main. That was reverted in PR #4798 (commit 181d8b7399), restoring the dist artifacts but also removing this page. This commit re-adds the page (content identical to the original) on a clean branch base. Verified diff stat shows only 3 file changes (docs/reference/whats-new-in-3-1.mdx + docs.json nav entries + changeset) — no dist/ touch. Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/whats-new-in-3-1-restore.md | 9 ++ docs.json | 2 + docs/reference/whats-new-in-3-1.mdx | 191 +++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 .changeset/whats-new-in-3-1-restore.md create mode 100644 docs/reference/whats-new-in-3-1.mdx diff --git a/.changeset/whats-new-in-3-1-restore.md b/.changeset/whats-new-in-3-1-restore.md new file mode 100644 index 0000000000..a41f843b82 --- /dev/null +++ b/.changeset/whats-new-in-3-1-restore.md @@ -0,0 +1,9 @@ +--- +"adcontextprotocol": patch +--- + +Re-apply `docs/reference/whats-new-in-3-1.mdx` cleanly from current main after the original landing (PR #4784) was reverted to restore accidentally-deleted `dist/docs/` versioned snapshots. Content is identical to the original page — comprehensive 3.0 → 3.1 narrative covering 15 headline features synthesized from a full audit of every spec PR merged since 3.0.6. + +Adds two nav entries in `docs.json` under the existing **AdCP 3.0** groups. + +Closes the gap reopened by the revert. diff --git a/docs.json b/docs.json index 1448d20120..1581e80c06 100644 --- a/docs.json +++ b/docs.json @@ -85,6 +85,7 @@ "expanded": false, "pages": [ "docs/reference/whats-new-in-v3", + "docs/reference/whats-new-in-3-1", "docs/reference/migration/v3-readiness", "docs/reference/migration/index", "docs/reference/migration/prerelease-upgrades", @@ -681,6 +682,7 @@ "expanded": false, "pages": [ "docs/reference/whats-new-in-v3", + "docs/reference/whats-new-in-3-1", "docs/reference/migration/v3-readiness", "docs/reference/migration/index", "docs/reference/migration/prerelease-upgrades", diff --git a/docs/reference/whats-new-in-3-1.mdx b/docs/reference/whats-new-in-3-1.mdx new file mode 100644 index 0000000000..89f1a18f24 --- /dev/null +++ b/docs/reference/whats-new-in-3-1.mdx @@ -0,0 +1,191 @@ +--- +title: What's New in AdCP 3.1 +description: "Adopter overview of AdCP 3.1 — distributed brand.json, dependency-impact webhooks, release-precision version negotiation, canonical creative formats, vendor-attested measurement, action discovery, and more. Additive over 3.0 — no breaking changes — but the new surfaces solve real production problems and adopters should upgrade as soon as their SDK pins 3.1." +"og:title": "AdCP — What's New in 3.1" +--- + +AdCP 3.1 is a minor release. Every 3.1 change is **additive** over 3.0: new fields are optional, no required field was removed, no shape changed in a way that breaks a 3.0-conformant client. **No breaking changes.** But schemas moved — 3.1 introduces meaningful new surfaces that solve production problems 3.0 left open. **You should bump your pin to 3.1 as soon as your SDK is ready** to pick up the new fields. + +For per-PR detail and migration tables, see [Release Notes § Version 3.1.0](/docs/reference/release-notes#version-3-1-0-unreleased). For long-form normative reference, follow the link on each headline below. + + +**Looking for the major v2 → v3 changes instead?** See [What's New in AdCP 3](/docs/reference/whats-new-in-v3). This page covers the 3.0 → 3.1 minor delta only. + + +## Why upgrade + +3.1 is the production-hardening release. 3.0 shipped the protocol surface — discovery, buy lifecycle, signals, creative library, brand identity. 3.1 closes the operational gaps that surfaced when real agents started running buys against real publishers: + +- **You can debug your webhooks now.** Buyer agents inspect their own recent delivery fires via `webhook_activity[]` on `get_media_buys` — HTTP status, fire time, idempotency_key — instead of guessing why their gateway returned 5xx. +- **You can see why a buy is impaired.** When a creative gets pulled, an audience is suspended, a catalog item is withdrawn, or an event source goes quiet, the buy's `health` flips to `impaired` and `impairments[]` lists every offline dependency with its package_ids and remediation hint. Both as a snapshot on `get_media_buys` and as a push fire via `notification-type: impairment`. +- **You can bind goals to vendor-attested measurement.** Optimization goals can now reference `(vendor, metric_id)` pairs from real measurement vendors (DV, IAS, Adelaide, TVision, Lumen, Kantar, Upwave, Scope3, etc.) — not vendor-agnostic strings the seller can interpret however they want. +- **You can pin a release and stop fighting drift.** Release-precision `adcp_version` (`"3.1"`, `"3.1-beta"`) on every request; sellers advertise their full `supported_versions` set and echo what they actually served. SDK constructor pins are now a real thing. +- **Sub-brands self-publish.** A brand can publish its own canonical `brand.json` on its own domain while the corporate house declares ownership via a portfolio pointer — same reciprocal pattern as IAB's `ads.txt` / `sellers.json`. +- **Creative formats have a canonical vocabulary.** 12 canonical `format_kind` values + a publisher catalog discovery surface + a projection-ref mechanism — no more per-publisher format spaghetti. +- **Action discovery is mechanical.** Products advertise `allowed_actions[]`; media buys carry `available_actions[]`. Buyers pre-flight which mutations are valid instead of failing mid-flight. +- **Billing has finality.** Row-level `is_final` + `finalized_at` on delivery; matching `final` + `finalized_at` + `measurement_window` on `report_usage`. Buyers know when the number stops moving and can reconcile invoices. + +Plus a long tail of error-code clarity, auth tightening, idempotency rules, TMP IdentityMatch upgrades, and `adagents.json` scaling work — see the headline list below. + +## At a glance + +| Area | 3.0 | 3.1 | +|---|---|---| +| **`brand.json`** | Inline `brands[]` under a single house document | Distributed: brands self-publish on their own domains; houses declare ownership via `brand_refs[]`; mutual-assertion trust; typed `trademarks[]` | +| **Brand verification** | brand.json discovery only | `verify_brand_claim` / `verify_brand_claims` — federated authoritative verification (partners can ask the brand if a claim belongs to it) | +| **Dependency impact** | No protocol surface for "a resource the buy depends on went offline" | `media_buy.health` + `impairments[]` snapshot; `notification-type: impairment` webhooks; `propagation_surfaces` capability; `impairment.coherence` compliance invariant | +| **Webhook foundation** | Specced per-feature | One persistent-channel contract: snapshot/log duality, `notification_id` typed at envelope, per-account + per-resource subscription model | +| **Webhook observability** | No buyer-side delivery visibility | `webhook_activity[]` on `get_media_buys` — buyers self-service debug their own missed fires | +| **Creative formats** | Format-by-name with per-publisher variants | 12 canonical `format_kind` values + publisher catalog discovery (`adagents.json formats[]`) + `v1_format_ref` for dual emission + size flexibility (fixed / multi-size / responsive) | +| **Version negotiation** | Integer `adcp_major_version` per request | Release-precision `adcp_version` (e.g. `"3.1"`) + `adcp.supported_versions` advertisement + envelope echo. Integer field remains as backwards-compatible legacy | +| **Optimization goals** | `event` + `metric` kinds, vendor-agnostic | New `vendor_metric` kind — bind goals to vendor-attested metrics; `vendor_metric_optimization` per-product capability; three-precondition rejection rule | +| **Capability declarations** | Per-protocol basics | New: `supported_optimization_metrics`, `supported_target_kinds`, `media_buy.frequency_capping`, `media_buy.propagation_surfaces`, `creative.bills_through_adcp`, `capabilities.idempotency.in_flight_max_seconds` | +| **Delivery reporting** | `reach` without window semantics; viewability is rate only | `reach_window` (cumulative / period / rolling); `viewability.viewed_seconds`; windowed pull recovery via `time_granularity` + `include_window_breakdown` | +| **Billing surface** | Authority via `billing_measurement`; no finality marker | Row-level `is_final` + `finalized_at` on delivery; `final` + `finalized_at` + `measurement_window` on `report_usage`; `creative.bills_through_adcp` capability + `BILLING_OUT_OF_BAND` error | +| **Action discovery** | No structured action vocabulary on buys/products | `allowed_actions[]` on Product (advisory template); `available_actions[]` on `get_media_buys` / `create_media_buy` / `update_media_buy` responses | +| **Auth + security** | Single `AUTH_REQUIRED` error; no transport-channel rule | `AUTH_REQUIRED` split into `AUTH_MISSING` (correctable) + `AUTH_INVALID` (terminal); `CREDENTIAL_IN_ARGS` rejects credentials in request payload; request-signing `protocol_methods_*` namespace | +| **Idempotency** | Per-call replay only | Rule 9 (concurrent retries) + Rule 10 (downstream reconciliation); `IDEMPOTENCY_IN_FLIGHT` error code; `capabilities.idempotency.in_flight_max_seconds` | +| **Async envelope** | Two-shape submitted envelope for create-style tasks | Three-shape envelope extended to `sync_audiences` | +| **TMP IdentityMatch** | Basic request/response | `serve_window_sec` frequency-cap data flow; `seller_agent_url` required on request; optional `package_ids` | +| **`adagents.json`** | Authoritative-only discovery | Managed-network scale (20 MB cap + `publisher_domains[]` compact form); ads.txt `managerdomain` fallback; tightened `revoked_publisher_domains[]` semantics | +| **Schema housekeeping** | — | `x-adcp-hoist` opt-in marker; `allowed_values` on text-asset-requirements; `vast_tracker` + `daast_tracker` asset types; optional `currency`/`total_budget` on create/update responses | +| **Compliance suite** | Per-tool scenarios | Capability-gated scenarios for `frequency_cap_enforcement`, `per_creative_attribution`, `metric_mode`, ROAS, `audience_buy_flow`, `event_dedup_flow`, `performance_buy_flow`; storyboard `requires` runtime gate; `comply_test_controller` sandbox gate | + +## Headline features + +### Distributed `brand.json` — sub-brands self-publish + +A brand can now publish its **own** canonical `brand.json` on its own domain while the corporate house declares ownership via a portfolio pointer (`brand_refs[]`). The hierarchy stays one level deep — only houses declare ownership. Trust resolves via mutual assertion: both sides reciprocate. Identity attributes (logos, colors, tone, tagline) trust on the leaf's TLS alone; relationship trust (governance propagation, billable inclusion) gates on the reciprocal entry. + +Same shape as IAB's `ads.txt` / `sellers.json` / `app-ads.txt` reciprocal-publication pattern, applied to brand identity. Plus: typed `trademarks[]` with optional `status`, `license_type`, `licensor_domain`, `countries`, `nice_classes` (cross-industry disambiguation). Compliance fields resolve strictest-of (brand-level can tighten, never weaken) while identity fields stay brand-wins. + +→ Normative spec: [`brand.json` § Distributed publishing](/docs/brand-protocol/brand-json#distributed-publishing) · PR [#4505](https://github.com/adcontextprotocol/adcp/pull/4505) + +### `verify_brand_claim` / `verify_brand_claims` — federated brand verification + +Three new brand-protocol tasks let partners ask a brand authoritatively whether a claim belongs to it: a brand agent published at the brand's own domain, queried by anyone who needs to verify trademark ownership, ad-creative claims, or asset rights. Federated by design — every brand agent answers for its own brand only. Reframes the email-based self-healing SHOULD from #4505 as a richer pull-based DRM-for-brand-identity surface. + +→ Spec: [Brand Protocol § verify_brand_claim](/docs/brand-protocol/tasks/verify_brand_claim) · PRs [#4540](https://github.com/adcontextprotocol/adcp/pull/4540), [#4603](https://github.com/adcontextprotocol/adcp/pull/4603) + +### Dependency-impact webhooks and snapshot coherence + +When a resource a media buy depends on transitions to an offline state — an audience suspended, a creative rejected post-approval, a catalog item withdrawn, an event source quiet, a property depublished — buyers see it through two parallel surfaces: + +- **Snapshot.** `media_buy.health` flips from `ok` to `impaired`; `media_buy.impairments[]` lists every offline resource with its package_ids, transition, reason_code, and remediation hint. The next `get_media_buys` read shows current truth. +- **Log.** `notification-type: impairment` webhooks fire with `notification_id = impairment_id` and the same payload shape, configured via `push_notification_config`. + +Either path is complete; buyers reconcile via the snapshot when push and pull disagree. Sellers declare which surfaces they use via `capabilities.media_buy.propagation_surfaces` (`["snapshot"]`, `["webhook"]`, `["snapshot", "webhook"]`, or `["out_of_band"]`). The `impairment.coherence` compliance invariant grades the contract end-to-end (forward, inverse, and health-iff rules; relaxes on terminal-status buys). + +→ Spec: [Media Buy Lifecycle § Health & impairments](/docs/media-buy/media-buys/lifecycle#health-impairments) · [Snapshot and log contract](/docs/protocol/snapshot-and-log) · RFC #2853 · PRs #4588, #4601, #4677, #4685, #4690 + +### Webhook foundation + buyer-side delivery visibility + +3.1 codifies one persistent-channel contract for every push surface: snapshot is authoritative, push is at-least-once and unordered, dedupe via `idempotency_key`, correlate state via `notification_id` (now typed at the envelope level on `mcp-webhook-payload.json`), replay = re-read the snapshot. Future webhook RFCs reference the foundation instead of re-deriving it. The subscription model extends to per-account so creative-library-level events (creative state changes) fire even when no media buy directly references the creative. + +For production debugging, buyers can opt-in to `webhook_activity[]` on `get_media_buys` — recent fires for the buys they see, with HTTP status, fire time, and `idempotency_key`. No more black-box "the publisher fired but my gateway returned 5xx and I can't see it." Pure self-service: buyers debug their own integration without operator round-trips. + +→ Spec: [Snapshot and log contract](/docs/protocol/snapshot-and-log) · [Webhooks § Persistent channel contract](/docs/building/by-layer/L3/webhooks#persistent-channel-contract) · RFC #4582 · PRs #4601, #4701, #4730 + +### Canonical creative formats — live, 12 canonicals, backwards-compatible + +Live in 3.1, additive over 3.0. Products carry `format_options[]`: a list of `ProductFormatDeclaration` entries with a `format_kind` discriminator from the canonical enum. **12 canonicals:** `image`, `html5`, `display_tag`, `video_hosted`, `video_vast`, `audio_hosted`, `audio_daast`, `image_carousel`, `responsive_creative`, `sponsored_placement`, `agent_placement`, `custom` — plus `native_in_feed`. Three of those (`sponsored_placement`, `responsive_creative`, `agent_placement`) are tagged **experimental** within the framework; the rest are **stable**. The promotion queue for new canonicals is tracked in [#3666](https://github.com/adcontextprotocol/adcp/issues/3666). + +**Backwards compatibility.** The v1 `format_ids` path still works. `ProductFormatDeclaration` carries an optional `v1_format_ref: [{agent_url, id}]` array so v2 declarations link to one or more v1 named formats — sellers can dual-emit during the migration window. SDKs treat the enum as **open at parse time**: unknown future canonicals don't fail validation; they're surfaced as `runtime_status: declared_only` for routing purposes. + +**Publisher catalogs.** `list_creative_formats(publisher_domain="…")` returns the publisher's authoritative format list by reading `/.well-known/adagents.json formats[]`, falling back to the AAO community mirror, then to agent-derived. Response carries `source: "publisher" | "aao_mirror" | "agent_derived"` so buyers know which tier produced the list. + +**Size flexibility.** Display canonicals declare size in three modes: fixed (`width`+`height`), multi-size (`sizes: [{w,h}]` — mirrors OpenRTB `banner.format[]`), or responsive (`min_width`/`max_width`/`min_height`/`max_height`). Mutually exclusive. + +→ Spec: [Canonical formats](/docs/creative/canonical-formats) · PRs [#3307](https://github.com/adcontextprotocol/adcp/pull/3307), [#4770](https://github.com/adcontextprotocol/adcp/pull/4770) + +### Release-precision version negotiation — pin your release + +Every request and response now carries `adcp_version` (release-precision: `"3.1"`, `"3.1-beta"`); sellers advertise their full `supported_versions` set on `get_adcp_capabilities` and echo the release they actually served at the envelope root. SDKs pin via a constructor option (`adcpVersion: "3.1"` JS, `adcp_version="3.1"` Python, `WithAdcpVersion("3.1")` Go) and emit both the new string and the integer `adcp_major_version` mirror for compatibility with sellers that only read the legacy field. The integer remains functional through all of 3.x — additive ship, no required changes for 3.0-conformant agents. `VERSION_UNSUPPORTED` is typed with `error.data.supported_versions[]` echoed so retry doesn't require an out-of-band lookup. + +→ Spec: [Versioning § Version negotiation](/docs/reference/versioning#version-negotiation) · PR [#3493](https://github.com/adcontextprotocol/adcp/pull/3493) + +### Vendor-attested measurement — `vendor_metric` goals + per-product capabilities + +Optimization goals now support a third `kind: "vendor_metric"` shape — bind goals to vendor-attested metrics like attention (DV, IAS, Adelaide, TVision, Lumen), panel-based brand lift (Kantar, Upwave, Cint), emissions (Scope3, Good-Loop), and retail-media partner metrics. Closes the gap where 3.0's vendor-agnostic enum values like `attention_seconds` were meaningless without a vendor binding. + +Sellers declare per-product `vendor_metric_optimization` with `supported_metrics[]` (the `(vendor, metric_id)` pairs the bidding stack can steer toward). A three-precondition rejection rule on goal acceptance — discovery, capability, reporting coherence — ensures the goal is steerable AND reportable end-to-end. Plus seller-level `supported_optimization_metrics` and `supported_target_kinds` on `conversion_tracking` for capability-gated compliance scenarios. + +→ Spec: [Optimization goals § `vendor_metric` kind](/docs/media-buy/media-buys/optimization-reporting#vendor-metric-goals) · PRs [#4668](https://github.com/adcontextprotocol/adcp/pull/4668), [#4669](https://github.com/adcontextprotocol/adcp/pull/4669), [#4649](https://github.com/adcontextprotocol/adcp/pull/4649) + +### Delivery reporting — `reach_window`, `viewed_seconds`, windowed pulls + +Three additive surfaces close reporting gaps. **`reach_window`** declares the measurement window for `reach` and `frequency` (cumulative / period / rolling) — buyers MUST NOT sum reach across rows without it. **`viewability.viewed_seconds`** reports average in-view duration per measurable impression, the reporting-side counterpart to the `viewed_seconds` optimization goal. **Windowed pull recovery** on `get_media_buy_delivery` accepts `time_granularity` + `include_window_breakdown: true`, returning `windows[]` slices shape-aligned with `reporting_webhook` payloads at the same granularity — a buyer who missed a webhook fire reconstructs identical data by polling. Capability-scoped via `reporting_capabilities.windowed_pull_granularities`; sellers can honestly declare asymmetric webhook-vs-pull frequencies. + +→ Spec: [Delivery metrics reference](/docs/media-buy/task-reference/get_media_buy_delivery) · PRs [#4618](https://github.com/adcontextprotocol/adcp/pull/4618), [#4601](https://github.com/adcontextprotocol/adcp/pull/4601) + +### Billing surface — authority, finality, and out-of-band + +Two complementary changes close the billing-grade reporting story. **Authority + finality flags:** `get_media_buy_delivery` responses now carry row-level `is_final` + `finalized_at` on `media_buy_deliveries[*]` and on each `by_package[*]` — buyers know when a number stops moving and is safe for invoice reconciliation. Symmetric on `report_usage`: each usage record carries `final` (default `true`), `finalized_at`, and `measurement_window`. **`bills_through_adcp` + `BILLING_OUT_OF_BAND`:** creative agents declare via `capabilities.creative.bills_through_adcp` whether they bill on-protocol or out-of-band (flat license, SaaS, bundled enterprise — CM360 is the canonical case). Buyers pre-filter; sellers in out-of-band mode reject `report_usage` calls with the new `BILLING_OUT_OF_BAND` error rather than silently accepting. + +→ Spec: [Billing measurement](/docs/media-buy/advanced-topics/accountability#billing-measurement) · [`report_usage`](/docs/accounts/tasks/report_usage) · PRs [#4735](https://github.com/adcontextprotocol/adcp/pull/4735), [#4561](https://github.com/adcontextprotocol/adcp/pull/4561) + +### Action discovery — `allowed_actions` and `available_actions` + +Structured action vocabulary for buy lifecycle mutations. Products advertise `allowed_actions[]` as an advisory template (which mutations the product *generally* supports, with `modes[]` and `allowed_statuses[]`). Media buys carry `available_actions[]` on `get_media_buys` / `create_media_buy` / `update_media_buy` responses — the current set of valid mutations for *this* buy in its current state. Buyers pre-flight which mutations are valid instead of issuing a call and getting `INVALID_STATE`. Finer-grained values added to `media-buy-valid-action` enum; legacy coarse values retained through 3.x for backwards compat (removed in 4.0). + +→ Spec: [Media Buy Lifecycle § Action discovery](/docs/media-buy/media-buys/lifecycle#action-discovery) · PR [#4514](https://github.com/adcontextprotocol/adcp/pull/4514) + +### Auth + security tightening + +Four complementary changes: **`AUTH_REQUIRED` split** into `AUTH_MISSING` (correctable — retry with credentials) and `AUTH_INVALID` (terminal — credentials presented and rejected; rotate or escalate; do NOT auto-retry). Recovery classifications now match operator reality. **`CREDENTIAL_IN_ARGS`** new error code: sellers MUST reject requests that smuggle buyer-principal credentials into the task payload instead of the transport authentication channel — closes a prompt-injection exfiltration surface. **Request-signing `protocol_methods_*` namespace** — RFC 9421 signing scope tightened to the AdCP method surface only. **`comply_test_controller` sandbox gate** — every controller call MUST carry `account.sandbox: true` and the seller MUST verify against its persisted account record, not trust the field. Defense-in-depth boundary between sandbox and production. + +→ Spec: [Error handling § Recovery Classification](/docs/building/by-layer/L3/error-handling#recovery-classification) · PRs [#3739](https://github.com/adcontextprotocol/adcp/pull/3739), [#4057](https://github.com/adcontextprotocol/adcp/pull/4057), [#4326](https://github.com/adcontextprotocol/adcp/pull/4326), [#4382](https://github.com/adcontextprotocol/adcp/pull/4382)/[#4392](https://github.com/adcontextprotocol/adcp/pull/4392) + +### Idempotency — Rules 9 + 10 + `IDEMPOTENCY_IN_FLIGHT` + +Two new rules close production-edge cases. **Rule 9 (concurrent retries):** when a buyer retries before the original call has produced a cached response, the seller MAY return `IDEMPOTENCY_IN_FLIGHT` (new error code) instead of blocking — useful when the first call invokes a slow downstream system (SSP, ad server, payment provider). Buyers MUST treat it as transient and MUST NOT mint a fresh `idempotency_key`. **Rule 10 (downstream reconciliation):** explicit guidance on how buyers reconcile when an `IDEMPOTENCY_EXPIRED` response arrives and they have evidence the original succeeded — perform a natural-key check (e.g., `get_media_buys` by `context.internal_campaign_id`) before generating a fresh key. **`capabilities.idempotency.in_flight_max_seconds`** new capability — seller declares how long an in-flight call can take so buyers tune retry pacing. + +→ Spec: [Calling an agent § Idempotency](/docs/protocol/calling-an-agent) · PRs [#4402](https://github.com/adcontextprotocol/adcp/pull/4402), [#4409](https://github.com/adcontextprotocol/adcp/pull/4409) + +### TMP IdentityMatch upgrades + +Three additive changes: **`serve_window_sec`** new required field on responses (1–300 seconds) — router caches the eligibility decision for this many seconds before re-querying. Replaces the prior `ttl_sec` framing with frequency-cap-data-flow-aware semantics. **`seller_agent_url`** now required on requests so the router can route the decision back to the originating seller. **`package_ids`** moved from required to optional — routers can ask "is this user eligible at all?" without enumerating packages. + +→ Spec: [TMP IdentityMatch implementation](/docs/trusted-match/identity-match-implementation) · PRs [#4070](https://github.com/adcontextprotocol/adcp/pull/4070), [#3687](https://github.com/adcontextprotocol/adcp/pull/3687) + +### `adagents.json` — managed-network scale, manager-domain fallback, revocation semantics + +Three production-scale improvements. **Managed-network scale:** authoritative `adagents.json` now caps at 20 MB; managers publishing large agent networks switch to a compact `publisher_domains[]` form that lists owned domains without inlining every property. **Manager-domain fallback:** when a publisher's authoritative `adagents.json` is absent, crawlers fall back to the `managerdomain` declared in `ads.txt` — closes the discovery gap for S3 / CloudFront-hosted publishers that can't return a 404 directly. **Revocation semantics:** `revoked_publisher_domains[]` is now strictly time-bound — revocation is a published fact with a discoverable timestamp, not a silent removal. Tightens trust propagation across the managed network. + +→ Spec: [`adagents.json` reference](/docs/governance/property/adagents) · PRs [#4504](https://github.com/adcontextprotocol/adcp/pull/4504), [#4173](https://github.com/adcontextprotocol/adcp/pull/4173), [#4536](https://github.com/adcontextprotocol/adcp/pull/4536) + +### Compliance suite — capability-gated scenarios + +Capability-gated storyboard scenarios let sellers run *only* what they claim. New scenarios for `frequency_cap_enforcement`, `per_creative_attribution`, `metric_mode` + ROAS (using a `contains:` matcher), `audience_buy_flow`, `event_dedup_flow`, and `performance_buy_flow` (capability-gated CPA buys). Plus a new `requires` runtime gate on Storyboards that conditions execution on declared capabilities — no more all-or-nothing scenarios. The full set is enumerated on the [Compliance catalog](/docs/building/verification/compliance-catalog). + +→ Spec: [Compliance catalog](/docs/building/verification/compliance-catalog) · PRs [#4312](https://github.com/adcontextprotocol/adcp/pull/4312), [#4642](https://github.com/adcontextprotocol/adcp/pull/4642), [#4664](https://github.com/adcontextprotocol/adcp/pull/4664), [#4722](https://github.com/adcontextprotocol/adcp/pull/4722), [#4727](https://github.com/adcontextprotocol/adcp/pull/4727), [#4731](https://github.com/adcontextprotocol/adcp/pull/4731) + +### Misc schema additions + +**`media_buy.frequency_capping` capability declaration** (#4670) — seller declares which frequency-cap surfaces it honors. **`x-adcp-hoist` opt-in marker** (#4630) — canonically-shared object schemas declare themselves as hoistable into shared types. **`allowed_values` on text-asset-requirements** (#4333) — closed-set text assets (CTA, etc.) declare the allowed values so buyers can constrain generation. **`vast_tracker` + `daast_tracker` asset types** (#3051) — video and audio tracker assets. **Optional `currency` + `total_budget`** on `create_media_buy` / `update_media_buy` success responses (#4417). **Async envelope to `sync_audiences`** (#4571) — three-shape submitted envelope (Success / Error / Submitted) extended from create-style tasks. + +→ See [Release Notes § Version 3.1.0](/docs/reference/release-notes#version-3-1-0-unreleased) for the per-PR detail. + +## Adopter action + +| If you are… | What you need to do | +|---|---| +| A 3.0-conformant production agent | Nothing required — 3.1 changes are additive and a 3.0 client keeps working. **But you should bump your SDK pin to 3.1 as soon as it's available** to pick up the new fields and emit `adcp_version` for forward-compatibility with future minors. | +| A buyer running production campaigns | Bump your SDK; pass `adcpVersion: "3.1"` (or your release) on construction. Implement `webhook_activity[]` reads when you suspect a missed fire. Read `media_buy.health` + `impairments[]` on every `get_media_buys` poll. Implement the `impairment.coherence` invariant in your reconciliation pipeline. | +| A seller running production buys | Surface `media_buy.health` + `impairments[]` whenever a referenced resource transitions offline. Declare `capabilities.media_buy.propagation_surfaces` honestly. Implement `webhook_activity[]` so buyers can debug their integration end-to-end without operator round-trips — buyer self-service is integration-friction reduction, not seller charity. Mark `is_final` on every delivery row so buyers know when to reconcile. | +| A sub-brand team that wants self-publish authority | Stand up `/.well-known/brand.json` at your own domain as a Brand Canonical Document. Declare `house_domain: ""`. Ask the parent house team to reciprocate via `brand_refs[]`. | +| A measurement vendor (attention, brand lift, emissions, retail) | Publish your `measurement.metrics[]` catalog at your AdCP agent. Sellers declaring `vendor_metric_optimization` per product can now bind optimization goals to your `(vendor, metric_id)` pairs. | +| A creative agent | Declare `capabilities.creative.bills_through_adcp` honestly. If you bill out of band, reject `report_usage` calls with `BILLING_OUT_OF_BAND` rather than silently accepting. | +| An SDK author | Pin `published_version` to a 3.1 release; emit `adcp_version` (release-precision string) plus `adcp_major_version` (integer mirror); normalize semver values to release-precision before wire emission (`"3.1.0-beta.1"` → `"3.1-beta.1"`); surface `VERSION_UNSUPPORTED` as a typed error rather than auto-downshifting. | + +## Migration + +**Bottom line: no breaking changes; additive only; you should bump.** + +All 3.1 changes are **additive** over 3.0. New fields are optional, no required field was removed, and no shape changed in a way that breaks a 3.0-conformant client. A buyer running against a 3.1 seller without upgrading their SDK keeps working — they just won't see the new fields. A seller running 3.0 schemas against a 3.1 buyer keeps working — the buyer's new fields are silently ignored. + +But the new surfaces solve real production problems, and the longer you stay on 3.0 the more you're operating without the production-hardening 3.1 added: webhook delivery debug, dependency-impact observability, billing finality flags, action discovery, vendor-attested measurement, release-precision negotiation. **Bump as soon as your SDK is ready.** + +The one publisher-visible behavior change is on `brand.json` `trademarks[]`: free-text `status` / `countries` values now validate against typed enum / ISO 3166-1 alpha-2 — non-conforming values surface as schema errors. If your `trademarks[]` published unrestricted free text, normalize the values before the 3.1 GA cut. + +For the per-PR detail, see [Release Notes § Version 3.1.0](/docs/reference/release-notes#version-3-1-0-unreleased). For the version-negotiation cadence and the 3.1 → 3.2 → 4.0 timeline, see [Versioning & Governance § Migration timeline](/docs/reference/versioning#migration-timeline).