diff --git a/.changeset/property-governance-protocol.md b/.changeset/property-governance-protocol.md
new file mode 100644
index 000000000..882945edc
--- /dev/null
+++ b/.changeset/property-governance-protocol.md
@@ -0,0 +1,9 @@
+---
+"adcontextprotocol": minor
+---
+
+Add Property Governance Protocol support to get_products
+
+- Add optional `property_list` parameter to get_products request for filtering products by property list
+- Add `property_list_applied` response field to indicate whether filtering was applied
+- Enables buyers to pass property lists from governance agents to sales agents for compliant inventory discovery
diff --git a/docs.json b/docs.json
index cf2262d30..ca5b1d84a 100644
--- a/docs.json
+++ b/docs.json
@@ -80,13 +80,6 @@
"docs/protocols/envelope-examples"
]
},
- {
- "group": "adagents.json",
- "pages": [
- "docs/media-buy/capability-discovery/adagents",
- "docs/media-buy/capability-discovery/authorized-properties"
- ]
- },
{
"group": "Signals Protocol",
"pages": [
@@ -101,6 +94,36 @@
}
]
},
+ {
+ "group": "Governance Protocol [3.0]",
+ "pages": [
+ "docs/governance/index",
+ {
+ "group": "Property Governance",
+ "pages": [
+ "docs/governance/property/index",
+ "docs/governance/property/specification",
+ "docs/governance/property/authorized-properties",
+ "docs/governance/property/adagents",
+ {
+ "group": "Tasks",
+ "pages": [
+ "docs/governance/property/tasks/index",
+ "docs/governance/property/tasks/list_property_features",
+ "docs/governance/property/tasks/property_lists",
+ "docs/governance/property/tasks/validate_property_delivery"
+ ]
+ }
+ ]
+ },
+ {
+ "group": "Brand Standards",
+ "pages": [
+ "docs/governance/brand-standards/index"
+ ]
+ }
+ ]
+ },
{
"group": "Curation Protocol",
"pages": [
diff --git a/docs/governance/brand-standards/index.mdx b/docs/governance/brand-standards/index.mdx
new file mode 100644
index 000000000..15a738adc
--- /dev/null
+++ b/docs/governance/brand-standards/index.mdx
@@ -0,0 +1,90 @@
+---
+sidebar_position: 1
+title: Brand Standards
+---
+
+
+**AdCP 3.0 Proposal** - This protocol is under development for AdCP 3.0. Feedback welcome via [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions).
+
+
+Brand Standards governance enables buyers to declare brand requirements that governance agents use to automatically apply appropriate rules across advertising workflows.
+
+## Overview
+
+Brand Standards addresses three concerns:
+
+| Concern | Description | Mechanism |
+|---------|-------------|-----------|
+| **Brand Identity** | Who is the advertiser? | Brand manifest declarations |
+| **Creative Guidelines** | What can the brand show? | Creative constraints and policies |
+| **Audience Constraints** | Who should see the ads? | Industry-specific targeting rules |
+
+## Brand Manifest
+
+The brand manifest is the core declaration that buyers provide to governance agents:
+
+```json
+{
+ "brand_manifest": {
+ "brand_name": "ToyBrand",
+ "industry": "toys",
+ "target_audience": "children_under_13",
+ "prohibited_categories": ["gambling", "alcohol", "tobacco"],
+ "creative_guidelines": {
+ "max_animation_duration": 15,
+ "require_accessibility": true
+ }
+ }
+}
+```
+
+When governance agents receive a brand manifest, they automatically apply:
+
+- **Regulatory compliance**: COPPA for children's brands, GDPR consent requirements
+- **Content filtering**: Block categories inappropriate for the brand's audience
+- **Creative validation**: Enforce brand-specific creative guidelines
+
+## Integration with Property Governance
+
+Brand manifests work alongside property lists. When creating a property list, include the brand manifest to get automatic rule application:
+
+```json
+{
+ "tool": "create_property_list",
+ "arguments": {
+ "name": "ToyBrand Q1 Properties",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "kidsmedia.com",
+ "tags": ["kids_family", "educational"]
+ }
+ ],
+ "filters": {
+ "countries_all": ["US"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ { "feature_id": "brand_safety", "min_value": 90, "max_value": 100 }
+ ]
+ },
+ "brand_manifest": {
+ "brand_name": "ToyBrand",
+ "industry": "toys",
+ "target_audience": "children_under_13"
+ }
+ }
+}
+```
+
+The governance agent uses the brand manifest to apply COPPA requirements, filter out non-child-safe properties, and enforce relevant industry rules.
+
+## Coming Soon
+
+Detailed specification and task definitions for Brand Standards governance are under development. Key planned features:
+
+- **Brand manifest validation**: Verify manifest completeness and consistency
+- **Industry rule sets**: Pre-defined rules for common industries (toys, alcohol, pharmaceuticals, finance)
+- **Creative compliance**: Validate creatives against brand guidelines
+- **Audience verification**: Ensure targeting complies with brand audience constraints
+
+See the [Governance Protocol overview](/docs/governance/index) for the broader context of how Brand Standards fits into AdCP governance.
diff --git a/docs/governance/index.mdx b/docs/governance/index.mdx
new file mode 100644
index 000000000..f08e368f2
--- /dev/null
+++ b/docs/governance/index.mdx
@@ -0,0 +1,187 @@
+---
+sidebar_position: 1
+title: Governance Protocol
+---
+
+
+**Draft for AdCP 3.0** - These governance protocols are under active development by the AAO Governance Working Group. Feedback welcome via [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions).
+
+
+The Governance Protocol provides standardized mechanisms for compliance, brand safety, and quality control across advertising workflows. It enables specialized agents to evaluate, filter, and score advertising entities against configurable criteria.
+
+## Mission
+
+> Define and operate transparent, privacy-safe, brand-suitable campaign governance that encodes human heuristics in open protocol objects, making campaigns easy to launch, safe to scale, and measurable against verifiable outcomes for buyers, sellers, and auditors.
+
+## Protocol Domains
+
+The Governance Protocol covers four distinct governance domains:
+
+| Domain | What It Governs | Key Mechanisms |
+|--------|-----------------|----------------|
+| **[Property Governance](/docs/governance/property/index)** | Where ads can run | Property lists, compliance filtering, adagents.json authorization |
+| **[Brand Standards](/docs/governance/brand-standards/index)** | Brand requirements | Brand manifests, creative guidelines, audience constraints |
+| **Creative Governance** | What creatives are compliant | Format validation, content moderation, accessibility |
+| **Campaign Governance** | What can be bought | Budget controls, approval workflows, policy compliance |
+
+Each domain has specialized governance agents that provide data, filtering, and scoring capabilities.
+
+## Working Group Areas
+
+The AAO Governance Working Group is developing standards across six areas that inform these protocols:
+
+| Area | Focus | Protocol Impact |
+|------|-------|-----------------|
+| Brand Safety & Suitability | Safe, appropriate ad contexts | Property Governance, Content filtering |
+| Brand Manifesto & Standards Library | Machine-readable brand policies | Brand Standards |
+| Creative Standards & Validation | Technical and ethical creative criteria | Creative Governance |
+| Process & Human Oversight | Human-in-the-loop checkpoints | All protocols |
+| Regulatory & Compliance | Legal and regional frameworks | All protocols |
+| Measurement & Verification | Proving compliance | All protocols |
+
+## Architecture
+
+Governance agents operate across the **full campaign lifecycle**:
+
+- **Setup time**: Configure property lists, brand requirements, and compliance rules
+- **Real-time**: Influence bid-time decisions through cached rules and scoring
+- **Post-bid**: Measurement, verification, and reporting on compliance
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ BUYER AGENT │
+│ - Aggregates governance from specialized agents │
+│ - Issues auth_tokens for sellers to access governance data │
+│ - Source of truth for compliant lists and brand requirements │
+└───────────────────────────┬─────────────────────────────────────────────┘
+ │ create_property_list, webhooks
+ ┌───────────────────┼───────────────────┐
+ ▼ ▼ ▼
+┌───────────────┐ ┌───────────────┐ ┌───────────────┐
+│ Consent Agent │ │ Scope3 Agent │ │ Brand Safety │
+│ (Compliance) │ │ │ │ Agent │
+├───────────────┤ ├───────────────┤ ├───────────────┤
+│ consent_qual │ │ carbon_score │ │ content_cat │
+│ tcf_version │ │ climate_risk │ │ brand_risk │
+│ coppa_cert │ │ green_host │ │ sentiment │
+└───────────────┘ └───────────────┘ └───────────────┘
+ │
+ │ get_property_list (with auth_token)
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ SELLER AGENT (DSP/SSP) │
+│ - Caches resolved property lists for bid-time decisions │
+│ - Receives updates via webhooks when lists change │
+└─────────────────────────────────────────────────────────────────────────┘
+```
+
+## Common Patterns
+
+### Feature Discovery
+
+All governance agents advertise their capabilities via discovery tasks:
+
+```json
+{
+ "tool": "list_property_features",
+ "arguments": {}
+}
+```
+
+Response:
+```json
+{
+ "features": [
+ { "feature_id": "consent_quality", "type": "quantitative", "range": { "min": 0, "max": 100 } },
+ { "feature_id": "coppa_certified", "type": "binary" },
+ { "feature_id": "carbon_score", "type": "quantitative", "range": { "min": 0, "max": 100 } }
+ ]
+}
+```
+
+### Brand Manifest
+
+Buyers declare their brand identity, and governance agents automatically apply appropriate rules:
+
+```json
+{
+ "brand_manifest": {
+ "brand_name": "ToyBrand",
+ "industry": "toys",
+ "target_audience": "children_under_13"
+ }
+}
+```
+
+When a governance agent sees this manifest, it automatically applies COPPA requirements, content filtering for children's brands, and other industry-specific rules.
+
+### Prompt-Based Policies
+
+Governance protocols support **natural language prompts** for policy definitions rather than rigid keyword lists:
+
+```json
+{
+ "policy": "Avoid content about violence, controversial politics, or adult themes. Sports news is excellent. Entertainment is generally acceptable.",
+ "exclusions_prompt": "Block content containing hate speech, illegal activities, or ongoing litigation against our company."
+}
+```
+
+This enables AI-powered verification agents to understand context and nuance.
+
+### Webhook Notifications
+
+Governance agents notify subscribers when evaluations change:
+
+```json
+{
+ "webhook_url": "https://buyer.example.com/webhooks/list-changed",
+ "events": ["list_updated", "property_removed", "score_changed"]
+}
+```
+
+## Integration with Media Buy
+
+The Media Buy Protocol consumes governance data at multiple stages:
+
+- **Product discovery**: Pass `property_list_ref` to `get_products` to filter inventory
+- **Media buy creation**: Reference property lists to constrain where ads can run
+- **Authorization**: adagents.json validates agent authority to sell properties
+
+```json
+{
+ "tool": "get_products",
+ "arguments": {
+ "brief": "UK video inventory for Q1",
+ "property_list_ref": {
+ "agent_url": "https://buyer-agent.example.com",
+ "list_id": "pl_q1_uk_premium",
+ "auth_token": "..."
+ }
+ }
+}
+```
+
+## Getting Started
+
+**Buyers:**
+1. Subscribe to governance agents for property data and brand safety
+2. Create property lists with filters and brand manifests
+3. Aggregate results into final compliant lists
+4. Share references with sellers (with auth tokens)
+
+**Governance Agent Implementers:**
+1. Implement feature discovery tasks (`list_property_features`, etc.)
+2. Implement stateful list management (CRUD operations)
+3. Support webhooks to notify when evaluations change
+4. See the [Property Protocol Specification](/docs/governance/property/specification) for detailed implementation guidance
+
+## Protocol Sections
+
+
+
+ Control where ads can run with property lists, compliance filtering, and publisher authorization via adagents.json.
+
+
+ Define brand requirements including creative guidelines, audience constraints, and industry-specific rules.
+
+
diff --git a/docs/media-buy/capability-discovery/adagents.mdx b/docs/governance/property/adagents.mdx
similarity index 98%
rename from docs/media-buy/capability-discovery/adagents.mdx
rename to docs/governance/property/adagents.mdx
index 442dee180..2732d1d8f 100644
--- a/docs/media-buy/capability-discovery/adagents.mdx
+++ b/docs/governance/property/adagents.mdx
@@ -1,9 +1,9 @@
---
-sidebar_position: 8
+sidebar_position: 3
title: adagents.json Tech Spec
---
-The `adagents.json` file provides a standardized way for publishers to declare which sales agents are authorized to sell their advertising inventory. This specification addresses authorization transparency and helps prevent unauthorized reselling of publisher inventory.
+The `adagents.json` file provides a standardized way for publishers to declare their properties and authorize sales agents. This is the foundation of Property Governance - it defines what properties exist and who can sell them.
**[AdAgents.json Builder](https://adcontextprotocol.org/adagents)** - Validate existing files or create new ones with guided validation
diff --git a/docs/media-buy/capability-discovery/authorized-properties.mdx b/docs/governance/property/authorized-properties.mdx
similarity index 97%
rename from docs/media-buy/capability-discovery/authorized-properties.mdx
rename to docs/governance/property/authorized-properties.mdx
index 11d72b671..37981ec9e 100644
--- a/docs/media-buy/capability-discovery/authorized-properties.mdx
+++ b/docs/governance/property/authorized-properties.mdx
@@ -1,5 +1,6 @@
---
-title: Authorized Properties
+sidebar_position: 2.5
+title: Understanding Authorization
description: Understanding property authorization in AdCP - preventing unauthorized resale and validating sales agent relationships
keywords: [authorized properties, adagents.json, unauthorized resale, ads.txt, sales agent authorization, property validation]
---
@@ -268,11 +269,11 @@ For complete technical details on implementing the `adagents.json` file format,
- Validation code examples and error handling
- Security considerations and best practices
-See the **[adagents.json Tech Spec](/docs/media-buy/capability-discovery/adagents)** for complete implementation guidance.
+See the **[adagents.json Tech Spec](/docs/governance/property/adagents)** for complete implementation guidance.
## Related Documentation
- **[`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties)** - API reference for property discovery
- **[Product Discovery](/docs/media-buy/product-discovery/)** - How authorization integrates with product discovery
- **[Properties Schema](https://adcontextprotocol.org/schemas/v2/core/property.json)** - Technical property data model
-- **[adagents.json Tech Spec](/docs/media-buy/capability-discovery/adagents)** - Complete `adagents.json` implementation guide
\ No newline at end of file
+- **[adagents.json Tech Spec](/docs/governance/property/adagents)** - Complete `adagents.json` implementation guide
\ No newline at end of file
diff --git a/docs/governance/property/index.mdx b/docs/governance/property/index.mdx
new file mode 100644
index 000000000..d4f846877
--- /dev/null
+++ b/docs/governance/property/index.mdx
@@ -0,0 +1,227 @@
+---
+sidebar_position: 1
+title: Overview
+---
+
+
+**AdCP 3.0 Proposal** - This protocol is under development for AdCP 3.0. Feedback welcome via [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions).
+
+
+Property Governance standardizes how advertising properties (websites, apps, CTV, podcasts, billboards) are identified, authorized, enriched with data, and selected for campaigns.
+
+## Overview
+
+Property Governance addresses four distinct concerns:
+
+| Concern | Question | Owner | Mechanism |
+|---------|----------|-------|-----------|
+| **Property Identity** | What properties exist? | Publishers | `adagents.json` properties array |
+| **Sales Authorization** | Who can sell this property? | Publishers | `adagents.json` authorized_agents |
+| **Property Data** | What do we know about this property? | Data providers | Governance agents via `list_property_features` |
+| **Property Selection** | Which properties meet my requirements? | Buyers | Property lists with filters |
+
+The first two are **publisher-side declarations** via adagents.json. The last two are **buyer-side operations** that consume property data from governance agents.
+
+## Publisher Side: adagents.json
+
+Publishers declare their properties and authorize sales agents via `/.well-known/adagents.json`:
+
+```json
+{
+ "$schema": "https://adcontextprotocol.org/schemas/v2/adagents.json",
+ "properties": [
+ {
+ "property_id": "example_site",
+ "property_type": "website",
+ "name": "Example Site",
+ "identifiers": [{"type": "domain", "value": "example.com"}]
+ }
+ ],
+ "authorized_agents": [
+ {
+ "url": "https://agent.example.com",
+ "authorized_for": "Official sales agent",
+ "authorization_type": "property_ids",
+ "property_ids": ["example_site"]
+ }
+ ]
+}
+```
+
+See the [adagents.json Tech Spec](/docs/governance/property/adagents) for complete documentation.
+
+## Buyer Side: Property Data and Selection
+
+### Property Data Providers
+
+Governance agents provide data about properties - compliance scores, brand safety ratings, sustainability metrics, etc. They advertise their capabilities via `list_property_features`:
+
+```json
+{
+ "features": [
+ { "feature_id": "consent_quality", "type": "quantitative", "range": { "min": 0, "max": 100 } },
+ { "feature_id": "coppa_certified", "type": "binary" },
+ { "feature_id": "carbon_score", "type": "quantitative", "range": { "min": 0, "max": 100 } }
+ ]
+}
+```
+
+Buyers send property lists to these agents, and the agents filter and score the properties based on their specialized data. Different agents specialize in different data:
+
+- **Compliance vendors** (data integrity, consent quality)
+- **Brand safety providers** (content classification, risk scoring)
+- **Quality measurement** (viewability, fraud detection)
+- **Sustainability providers** (carbon scoring, green hosting)
+
+### Property Selection via Governance Agents
+
+Buyers create **property lists on governance agents** - the agents manage these lists and apply their filtering logic:
+
+```json
+{
+ "tool": "create_property_list",
+ "arguments": {
+ "name": "Q1 Campaign - UK Premium",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news"]
+ }
+ ],
+ "filters": {
+ "countries_all": ["UK"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ { "feature_id": "consent_quality", "min_value": 85, "max_value": 100 }
+ ]
+ },
+ "brand_manifest": {
+ "brand_name": "ToyBrand",
+ "industry": "toys",
+ "target_audience": "children_under_13"
+ }
+ }
+}
+```
+
+When you provide a brand manifest, governance agents automatically apply appropriate rules (COPPA for children's brands, content filtering based on industry, etc.).
+
+A buyer agent typically works with **multiple governance agents** (consent, brand safety, sustainability) and aggregates/intersects their results into a final compliant list.
+
+## How It Fits Together
+
+```mermaid
+flowchart TB
+ subgraph Buyer["BUYER AGENT"]
+ B1[Source of truth for compliant list]
+ B2[Aggregates results from specialized agents]
+ B3[Issues auth_tokens for sellers]
+ end
+
+ subgraph Governance["GOVERNANCE AGENTS"]
+ CA["Consent Agent
consent_quality
tcf_version
coppa_certified"]
+ S3["Scope3 Agent
carbon_score
climate_risk
green_hosting"]
+ BS["Brand Safety Agent
content_category
brand_risk
sentiment"]
+ end
+
+ subgraph Seller["SELLER AGENT (DSP/SSP)"]
+ SE1[Caches resolved property lists]
+ SE2[Uses cached lists for bid-time decisions]
+ end
+
+ Buyer -->|create_property_list + webhooks| CA
+ Buyer -->|create_property_list + webhooks| S3
+ Buyer -->|create_property_list + webhooks| BS
+
+ Buyer -->|get_property_list with auth_token| Seller
+```
+
+## Sharing Property Lists with Sellers
+
+Once a buyer has a compliant property list, they share it with sellers:
+
+1. **Get a list reference**: The buyer agent exposes the list via `get_property_list`
+2. **Issue an auth token**: The buyer generates a token that authorizes access to the list
+3. **Pass to seller**: Include `property_list_ref` with `auth_token` in product discovery or media buy requests
+4. **Seller caches locally**: Sellers fetch and cache the resolved list for bid-time decisions
+5. **Webhooks for updates**: When the list changes, sellers are notified to refresh their cache
+
+```json
+{
+ "property_list_ref": {
+ "agent_url": "https://buyer-agent.example.com",
+ "list_id": "pl_q1_uk_premium",
+ "auth_token": "eyJhbGciOiJIUzI1NiIs..."
+ }
+}
+```
+
+Sellers use this reference in `get_products` to filter available inventory:
+
+```json
+{
+ "tool": "get_products",
+ "arguments": {
+ "brief": "UK video inventory for Q1",
+ "property_list_ref": {
+ "agent_url": "https://buyer-agent.example.com",
+ "list_id": "pl_q1_uk_premium",
+ "auth_token": "..."
+ }
+ }
+}
+```
+
+## Relationship to Other Protocols
+
+### Property Governance + Media Buy
+
+The Media Buy Protocol consumes property lists at multiple stages:
+
+- **Product discovery**: Pass `property_list_ref` to `get_products` to filter inventory to compliant properties
+- **Media buy creation**: Reference property lists to constrain where ads can run
+- **Authorization**: adagents.json validates agent authority to sell
+
+### Property Governance + Signals
+
+Both protocols operate on properties but serve different purposes:
+
+| Signals Protocol | Property Governance |
+|------------------|---------------------|
+| Audience/contextual data | Property metadata and compliance |
+| "Who should see this ad?" | "Where can this ad run?" |
+| Signal activation | Property filtering |
+
+## Tasks
+
+### Discovery
+
+- **[list_property_features](/docs/governance/property/tasks/list_property_features)**: Discover what data a governance agent can provide about properties
+
+### Property List Management
+
+- **[create_property_list](/docs/governance/property/tasks/property_lists#create_property_list)**: Create a new property list on a governance agent
+- **[get_property_list](/docs/governance/property/tasks/property_lists#get_property_list)**: Retrieve resolved properties (with caching guidance)
+- **[update_property_list](/docs/governance/property/tasks/property_lists#update_property_list)**: Modify filters or base properties
+- **[delete_property_list](/docs/governance/property/tasks/property_lists#delete_property_list)**: Remove a property list
+
+## Getting Started
+
+**Publishers:**
+1. Create `/.well-known/adagents.json` with property definitions
+2. Authorize sales agents for your properties
+
+**Buyers:**
+1. Subscribe to governance agents for property data
+2. Create property lists on each governance agent with filters and brand manifests
+3. Aggregate results into a final compliant list
+4. Share property list references with sellers (with auth tokens)
+
+**Governance Agent Implementers:**
+1. Implement `list_property_features` to advertise your capabilities
+2. Implement property list CRUD operations
+3. Support webhooks to notify buyers when evaluations change
+4. See the [Protocol Specification](/docs/governance/property/specification) for implementation details
+
+See the [Protocol Specification](/docs/governance/property/specification) for detailed implementation guidance.
diff --git a/docs/governance/property/specification.mdx b/docs/governance/property/specification.mdx
new file mode 100644
index 000000000..343d1c7e3
--- /dev/null
+++ b/docs/governance/property/specification.mdx
@@ -0,0 +1,611 @@
+---
+sidebar_position: 2
+title: Protocol Specification
+---
+
+
+**AdCP 3.0 Proposal** - This specification is under development for AdCP 3.0. Feedback welcome via [GitHub Discussions](https://github.com/adcontextprotocol/adcp/discussions).
+
+
+**Status**: Request for Comments
+**Last Updated**: January 2026
+
+## Abstract
+
+The Property Protocol defines a standard Model Context Protocol (MCP) and Agent-to-Agent (A2A) interface for property identity, authorization, data provision, and selection. This protocol enables publishers to declare properties and authorized agents, data providers to offer property intelligence, and buyers to select compliant property sets.
+
+## Overview
+
+The Property Protocol addresses four distinct concerns:
+
+| Concern | Question | Owner | Mechanism |
+|---------|----------|-------|-----------|
+| **Property Identity** | What properties exist? | Publishers | `adagents.json` properties array |
+| **Sales Authorization** | Who can sell this property? | Publishers | `adagents.json` authorized_agents |
+| **Property Data** | What do we know about this property? | Data providers | Governance agents via `list_property_features` |
+| **Property Selection** | Which properties meet my requirements? | Buyers | Property lists with filters |
+
+The first two are **publisher-side declarations** via adagents.json. The last two are **buyer-side operations** that consume property data from governance agents.
+
+### Property Data and Selection
+
+Property data and selection use a **stateful** model:
+
+- **Feature discovery**: Agents advertise what they can evaluate via `list_property_features`
+- **Property list management**: CRUD operations for managed property lists with filters
+- **Brand manifests**: Let agents automatically apply rules based on brand characteristics
+- **Webhook notifications**: Real-time updates when resolved lists change
+- **Marketplace architecture**: Multiple specialized agents as subscription services
+
+All evaluation (scoring, filtering, discovery) happens implicitly when property lists are resolved via `get_property_list`.
+
+## Core Concepts
+
+### Request Roles and Relationships
+
+Every governance request involves two key roles:
+
+#### Orchestrator (Buyer Agent)
+The platform or system making the API request to the governance agent. In AdCP documentation, this role is often called a "buyer agent" when operating in the media buying context.
+- **Examples**: DSP, trading desk platform, campaign management tool
+- **Responsibilities**: Makes API calls, handles authentication, manages the technical interaction
+- **Account**: Has technical credentials and API access to the governance agent
+
+#### Principal
+The entity on whose behalf the request is being made:
+- **Examples**: Advertiser (Nike), agency (Omnicom), brand team
+- **Responsibilities**: Owns the campaign objectives and policy requirements
+- **Policies**: May have custom thresholds, blocklists, or compliance requirements
+
+### Property Identification
+
+Properties are identified using the standard AdCP property model:
+
+```json
+{
+ "property_type": "website",
+ "name": "Example News",
+ "identifiers": [
+ { "type": "domain", "value": "example.com" }
+ ]
+}
+```
+
+Property types include: `website`, `mobile_app`, `ctv_app`, `dooh`, `podcast`, `radio`, `streaming_audio`.
+
+### Property List References
+
+For large property sets, use property list references instead of embedding properties:
+
+```json
+{
+ "property_list_ref": {
+ "agent_url": "https://lists.example.com",
+ "list_id": "premium_news_sites",
+ "auth_token": "eyJhbGciOiJIUzI1NiIs..."
+ }
+}
+```
+
+The receiving agent fetches and caches the list independently, enabling:
+- **Scale**: Pass 50,000+ properties without payload bloat
+- **Updates**: Lists evolve without changing requests
+- **Authorization**: Token controls access to the list
+
+### Governance Agent Types
+
+#### Compliance Agents
+Specialized vendors providing property compliance intelligence:
+- **Examples**: Data integrity scoring, consent quality measurement
+- **Business Model**: Subscription or per-query pricing
+- **Methodology**: Published rubrics for transparency
+
+#### Brand Safety Agents
+Content classification and risk assessment:
+- **Examples**: Content categorization, brand safety scoring
+- **Coverage**: May specialize by channel or geography
+
+#### Quality Agents
+Performance and fraud measurement:
+- **Examples**: Viewability prediction, IVT detection
+- **Integration**: May correlate with campaign outcomes
+
+### Scoring and Data Privacy
+
+#### Scores Are Internal
+
+**Critical design principle**: Raw scores are NOT shared with buyers or downstream clients. This prevents data leakage.
+
+Governance agents maintain internal scoring models, but the protocol is designed around **list management**, not score exposure:
+
+- Buyers specify **thresholds** via `feature_requirements` (e.g., `"min_value": 85`)
+- Agents return **pass/fail lists** of properties that meet the thresholds
+- Raw scores never leave the governance agent
+
+This design prevents:
+- Score enumeration attacks (running lists with different thresholds to reverse-engineer scores)
+- Competitive intelligence leakage
+- Data arbitrage where buyers resell scoring data
+
+#### What Buyers Receive
+
+When calling `get_property_list`, buyers receive a compact list of identifiers (not full property objects) for efficiency:
+
+```json
+{
+ "list_id": "pl_abc123",
+ "identifiers": [
+ { "type": "domain", "value": "bbc.co.uk" },
+ { "type": "domain", "value": "theguardian.com" },
+ { "type": "domain", "value": "ft.com" }
+ ],
+ "total_count": 847
+}
+```
+
+Properties that pass the threshold are included. Properties that fail are excluded. No scores or property metadata are returned - just the identifiers needed for bid-time lookups.
+
+#### Methodology Discovery
+
+The `list_property_features` task returns information about what features an agent evaluates and their methodology, but NOT the underlying scores:
+
+```json
+{
+ "features": [
+ {
+ "feature_id": "consent_quality",
+ "name": "Consent Quality Score",
+ "type": "quantitative",
+ "range": { "min": 0, "max": 100 },
+ "methodology": "data_integrity_index",
+ "methodology_version": "v2.1",
+ "methodology_url": "https://compliance.example.com/methodology"
+ }
+ ]
+}
+```
+
+This allows buyers to:
+- Understand what an agent measures
+- Compare methodologies across agents
+- Set appropriate thresholds
+
+But they cannot retrieve the actual scores for individual properties.
+
+## Tasks
+
+### Discovery
+
+#### list_property_features
+
+Discover what features a governance agent can evaluate.
+
+**Use Cases**:
+- Capability discovery: Understand what an agent can evaluate
+- Marketplace browsing: Compare features across agents
+- Integration planning: Know what filters are available before creating lists
+
+### Property List Management
+
+#### create_property_list
+
+Create a new property list with filters and optional brand manifest.
+
+**Required Parameters**:
+- At least one country in `countries_all` (ISO country code)
+- At least one channel in `channels_any` (display, video, audio, etc.)
+
+**Base Properties**: An array of property sources to evaluate. Each entry is a discriminated union with `selection_type` as the discriminator:
+- **`publisher_tags`**: `{ "selection_type": "publisher_tags", "publisher_domain": "...", "tags": [...] }` - tags scoped to publisher
+- **`publisher_ids`**: `{ "selection_type": "publisher_ids", "publisher_domain": "...", "property_ids": [...] }` - property IDs scoped to publisher
+- **`identifiers`**: `{ "selection_type": "identifiers", "identifiers": [...] }` - no publisher context needed
+- **Omitted**: Query the agent's entire property database
+
+See the [base-property-source schema](https://adcontextprotocol.org/schemas/v2/property/base-property-source.json) for the full specification.
+
+**Filter Logic** (explicit in field names):
+- `countries_all`: Property must have feature data for **ALL** listed countries
+- `channels_any`: Property must support **ANY** of the listed channels
+- `feature_requirements`: Property must pass **ALL** requirements (AND)
+
+**Use Cases**:
+- Define compliant property sets with filters (country, channel, feature thresholds)
+- Provide brand manifest for automatic rule application
+- Register webhook URL for change notifications
+
+#### update_property_list
+
+Modify an existing property list.
+
+**Use Cases**:
+- Add or remove properties from base list
+- Adjust filters based on campaign needs
+- Update webhook URL
+
+#### get_property_list
+
+Retrieve a property list with resolved properties.
+
+**Use Cases**:
+- Get the current list of compliant properties after filters applied
+- Cache resolved list for bid-time use
+- Retrieve updated list after webhook notification
+
+#### list_property_lists
+
+List all property lists accessible to the authenticated principal.
+
+#### delete_property_list
+
+Remove a property list.
+
+### Validation
+
+#### validate_property_delivery
+
+Validates delivery records against a property list to determine compliance. Closes the loop between "what I wanted" and "what I got."
+
+Performs two independent validations:
+1. **Property compliance**: Is the identifier in the resolved property list?
+2. **Supply path authorization**: Was the sales agent authorized to sell that property? (optional, requires `sales_agent_url`)
+
+**Use Cases**:
+- Post-campaign validation: Verify impressions landed on compliant properties
+- Supply path verification: Confirm sales agents were authorized by publishers
+- Real-time monitoring: Check compliance rate during campaign execution
+- Audit trails: Generate compliance reports for regulatory or brand safety reviews
+
+**Property Validation Statuses**:
+- `compliant`: Identifier is in the resolved property list
+- `non_compliant`: Identifier is NOT in the resolved property list
+- `not_covered`: Identifier recognized but governance agent has no data for it (e.g., property too new)
+- `unidentified`: Identifier type not resolvable by this agent (e.g., detection failed, unsupported type)
+
+**Authorization Validation Statuses** (when `sales_agent_url` provided):
+- `authorized`: Sales agent is listed in publisher's adagents.json
+- `unauthorized`: Sales agent is NOT in publisher's authorized_agents list
+- `unknown`: Could not fetch or parse adagents.json
+
+**Unverifiable Records**: Both `not_covered` and `unidentified` records should be excluded when calculating compliance rates - you cannot penalize for detection gaps or coverage limitations. The distinction helps identify whether the gap is in the agent's data coverage vs the identifier resolution.
+
+**Response Format**: The response returns raw counts (compliant, non_compliant, not_covered, unidentified impressions). Consumers calculate rates as needed. Governance agents may optionally include an `aggregate` field with computed metrics (score, grade, label) - the format and meaning are agent-specific.
+
+## Typical Flows
+
+### Property List Flow
+
+Property lists enable buyers to define and manage compliant property sets:
+
+1. **Create property list**: Buyer defines list on governance agent with filters
+2. **Resolve and iterate**: Buyer calls `get_property_list` to see resolved properties
+3. **Share list reference**: Buyer provides `list_id` to orchestrator/seller
+4. **Cache locally**: Orchestrator/seller fetches and caches resolved properties
+5. **Use at bid time**: Orchestrator/seller uses local cache (no governance agent calls)
+6. **Refresh periodically**: Re-fetch based on `cache_valid_until` (typically 1-24 hours)
+
+**Important**: Governance agents are NOT in the real-time bid path. All bid-time decisions use locally cached property sets.
+
+### Webhook and Caching Pattern
+
+Webhooks provide **notification** that a property list has changed. The webhook payload contains a summary of changes, but you must call `get_property_list` to retrieve the actual updated properties.
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ Webhook Flow │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ 1. Governance agent re-evaluates properties (background) │
+│ 2. Webhook fires with change summary (added/removed counts) │
+│ 3. Recipient calls get_property_list to fetch updated list │
+│ 4. Recipient updates local cache │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+**Best Practices for Downstream Consumers**:
+
+Consumers of property lists (orchestrators, sellers, buyer agents) should implement **at least one** of these patterns:
+
+1. **Webhook-driven updates** (recommended): Register a webhook URL when creating the property list. Re-fetch via `get_property_list` when notified of changes.
+
+2. **Polling with cache hints**: Use `cache_valid_until` from `get_property_list` responses to schedule periodic re-fetches. Typical validity periods are 1-24 hours.
+
+3. **Hybrid approach**: Use webhooks for immediate updates, with polling as a fallback safety net.
+
+**Cache Expiry Guidance**:
+
+Every `get_property_list` response includes:
+- `resolved_at`: When the list was evaluated
+- `cache_valid_until`: When consumers should consider the cache stale
+
+```json
+{
+ "resolved_at": "2026-01-04T10:00:00Z",
+ "cache_valid_until": "2026-01-04T22:00:00Z"
+}
+```
+
+Consumers MUST NOT use cached data beyond `cache_valid_until` without re-fetching.
+
+### Property Discovery Flow
+
+1. **Define filters**: Specify country, channel, quality thresholds when creating property list
+2. **Resolve list**: Call `get_property_list` with `resolve=true` to get matching properties
+3. **Review candidates**: Evaluate returned properties for fit
+4. **Add to campaign**: Include property list reference in media buy
+
+## Response Structure
+
+All AdCP Governance responses follow a consistent structure:
+
+### Core Response Fields
+- **message**: Human-readable summary of the operation result
+- **context_id**: Session continuity identifier for follow-up requests
+- **data**: Task-specific payload (varies by task)
+
+### Protocol Transport
+- **MCP**: Returns complete response as flat JSON object
+- **A2A**: Returns as structured artifacts with message in text part, data in data part
+- **Data Consistency**: Both protocols contain identical AdCP data structures
+
+## Error Handling
+
+### Error Codes
+
+- `PROPERTY_NOT_FOUND`: Property identifier not recognized
+- `PROPERTY_NOT_MONITORED`: Governance agent doesn't cover this property
+- `POLICY_NOT_FOUND`: Referenced policy doesn't exist
+- `LIST_ACCESS_DENIED`: Cannot access property list (auth failed)
+- `LIST_NOT_FOUND`: Property list reference invalid
+- `METHODOLOGY_NOT_SUPPORTED`: Requested methodology version unavailable
+- `PARTIAL_RESULTS`: Some properties couldn't be evaluated
+
+### Partial Success
+
+For bulk operations, the response may include partial results:
+
+```json
+{
+ "message": "Evaluated 847 of 850 properties. 3 properties not in coverage.",
+ "context_id": "ctx-gov-123",
+ "scores": [...],
+ "errors": [
+ {
+ "code": "PROPERTY_NOT_MONITORED",
+ "property": { "identifiers": [{ "type": "domain", "value": "unknown.com" }] },
+ "message": "Property not in monitoring coverage"
+ }
+ ]
+}
+```
+
+## Implementation Notes
+
+### Caching Architecture
+
+Governance decisions are highly cacheable:
+
+#### Orchestrator-Side Caching
+- **Score cache**: Store scores with TTL from `valid_until` field
+- **Decision cache**: Pre-compute pass/fail for campaigns
+- **List cache**: Cache property lists from `property_list_ref`
+
+#### Agent-Side Caching
+- **Profile cache**: Maintain pre-computed property profiles
+- **Methodology cache**: Cache scoring algorithm results
+
+### Performance Requirements
+
+| Operation | Target Latency |
+|-----------|----------------|
+| Single property score | < 100ms |
+| Bulk scoring (100 properties) | < 2s |
+| Filter decision (cached) | < 5ms |
+| Property discovery | < 5s |
+
+### Multi-Agent Strategies
+
+Orchestrators may consult multiple governance agents:
+
+1. **Primary + Validation**: Use primary agent, validate with secondary
+2. **Specialization**: Route by property type to specialist agents
+3. **Consensus**: Require multiple agents to agree
+4. **Competitive**: Track agent accuracy, weight by performance
+
+## Agent Discovery
+
+Governance agents expose capabilities via `.well-known/agent-card.json`:
+
+```json
+{
+ "name": "Example Compliance Provider",
+ "url": "https://compliance.example.com",
+ "capabilities": {
+ "tasks": [
+ "list_property_features",
+ "create_property_list",
+ "get_property_list",
+ "update_property_list",
+ "delete_property_list",
+ "list_property_lists",
+ "validate_property_delivery"
+ ],
+ "protocols": ["MCP", "A2A"],
+ "schema_version": "v1"
+ },
+ "methodology": {
+ "documentation_url": "https://compliance.example.com/methodology",
+ "scoring_frameworks": ["data_integrity_index", "brand_safety_score"],
+ "coverage": {
+ "property_types": ["website", "mobile_app", "ctv_app"],
+ "jurisdictions": ["GDPR", "CCPA", "COPPA"]
+ }
+ }
+}
+```
+
+Use `list_property_features` for detailed capability discovery:
+
+```json
+{
+ "tool": "list_property_features",
+ "arguments": {}
+}
+```
+
+Returns the specific features the agent can evaluate (consent_quality, carbon_score, brand_risk, etc.).
+
+## Marketplace Architecture
+
+The Property Protocol enables a marketplace of specialized data agents:
+
+```
+┌─────────────────────────────────────────────────────────────────────────┐
+│ SELLER AGENT (DSP/SSP) │
+│ "Give me the compliant property list for this campaign" │
+└───────────────────────────────┬─────────────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────────────────────────┐
+│ BUYER AGENT (implements Property Protocol) │
+│ - Exposes: list_property_features, get_property_list, webhooks │
+│ - Source of truth for final compliant list │
+│ - Intersects results from specialized agents │
+└───────────────────────────┬─────────────────────────────────────────────┘
+ │
+ ┌───────────────────┼───────────────────┐
+ ▼ ▼ ▼
+┌───────────────┐ ┌───────────────┐ ┌───────────────┐
+│ Consent Agent │ │ Scope3 Agent │ │ Brand Safety │
+│ (Compliant) │ │ │ │ Agent │
+├───────────────┤ ├───────────────┤ ├───────────────┤
+│ Features: │ │ Features: │ │ Features: │
+│ consent_qual │ │ carbon_score │ │ content_cat │
+│ tcf_version │ │ climate_risk │ │ brand_risk │
+│ coppa_cert │ │ green_host │ │ sentiment │
+├───────────────┤ ├───────────────┤ ├───────────────┤
+│ Subscription │ │ Subscription │ │ Subscription │
+└───────────────┘ └───────────────┘ └───────────────┘
+```
+
+### Key Principles
+
+1. **Buyer agent is source of truth**: The buyer agent aggregates data from multiple specialized governance agents
+2. **Seller sees one interface**: Sellers interact only with the buyer agent using standard Property Protocol
+3. **Subscription model**: Each specialized agent is a paid service with its own features and coverage
+4. **Webhook-driven updates**: Specialized agents notify the buyer agent when property evaluations change
+
+### Multi-Agent Orchestration
+
+A buyer agent can distribute a master property list to multiple specialized agents:
+
+```python
+# Buyer agent creates variants on each specialized agent
+consent_list = consent_agent.create_property_list(
+ name="Q1 Campaign - Consent",
+ base_properties=master_list,
+ brand_manifest=brand_manifest
+)
+# Configure webhook for updates
+consent_agent.update_property_list(
+ list_id=consent_list.list_id,
+ webhook_url="https://buyer.example.com/webhooks/consent"
+)
+
+scope3_list = scope3_agent.create_property_list(
+ name="Q1 Campaign - Sustainability",
+ base_properties=master_list,
+ brand_manifest=brand_manifest
+)
+scope3_agent.update_property_list(
+ list_id=scope3_list.list_id,
+ webhook_url="https://buyer.example.com/webhooks/scope3"
+)
+
+# Buyer agent intersects filtered results
+def on_list_changed(event):
+ consent_props = consent_agent.get_property_list(consent_list.list_id, resolve=True)
+ scope3_props = scope3_agent.get_property_list(scope3_list.list_id, resolve=True)
+
+ # Intersection = properties that pass ALL governance agents
+ compliant_props = intersect(consent_props, scope3_props)
+
+ # Update buyer agent's exposed list
+ update_compliant_list(compliant_props)
+```
+
+### Brand Manifest
+
+Instead of specifying complex filters, buyers provide a brand manifest:
+
+```json
+{
+ "brand_manifest": {
+ "brand_name": "ToyBrand",
+ "industry": "toys",
+ "target_audience": "children_under_13",
+ "content_adjacency": ["kids_family", "education"],
+ "excluded_content": ["violence", "adult"]
+ }
+}
+```
+
+Each governance agent interprets the manifest according to their domain expertise:
+- **Consent agent**: Applies COPPA requirements for children_under_13
+- **Brand safety agent**: Filters to kids_family content, excludes violence/adult
+- **Sustainability agent**: Applies any green media requirements
+
+The buyer doesn't need to know the specific rules - they declare who they are, and agents figure out what applies.
+
+## Integration with Media Buy Protocol
+
+### Property Lists in Media Buys
+
+The Media Buy Protocol accepts property list references:
+
+```json
+{
+ "task": "create_media_buy",
+ "arguments": {
+ "packages": [{
+ "property_list_ref": {
+ "agent_url": "https://governance.example.com",
+ "list_id": "approved_q1_campaign",
+ "auth_token": "..."
+ }
+ }]
+ }
+}
+```
+
+### Policy Compliance
+
+Media buys can reference governance policies via property list references:
+
+```json
+{
+ "compliance_requirements": {
+ "property_list_ref": {
+ "agent_url": "https://compliance.example.com",
+ "list_id": "pl_q1_compliant",
+ "auth_token": "eyJhbGciOiJIUzI1NiIs..."
+ }
+ }
+}
+```
+
+## Best Practices
+
+1. **Cache aggressively**: Property scores change slowly; cache for hours/days
+2. **Bulk where possible**: Use batch operations for planning, not per-property calls
+3. **Pre-compute decisions**: Build pass/fail lookups before bid-time
+4. **Monitor coverage**: Track which properties agents don't cover
+5. **Log methodology versions**: For audit trails, record which scoring version was used
+6. **Handle partial results**: Not all properties will be scorable; plan for gaps
+
+## Next Steps
+
+- See the [adagents.json Tech Spec](/docs/governance/property/adagents) for property declaration and authorization
+- See the [list_property_features task reference](/docs/governance/property/tasks/list_property_features) for capability discovery
+- See the [Property List Management](/docs/governance/property/tasks/property_lists) for CRUD operations and webhooks
+- See the [validate_property_delivery task reference](/docs/governance/property/tasks/validate_property_delivery) for post-campaign compliance validation
diff --git a/docs/governance/property/tasks/index.mdx b/docs/governance/property/tasks/index.mdx
new file mode 100644
index 000000000..4679e3704
--- /dev/null
+++ b/docs/governance/property/tasks/index.mdx
@@ -0,0 +1,153 @@
+---
+sidebar_position: 1
+title: Task Reference
+---
+
+# Property Governance Tasks
+
+
+**AdCP 3.0 Proposal** - These tasks are under development for AdCP 3.0.
+
+
+Property governance uses a **stateful** model where all evaluation happens through property list management. Create lists with filters and brand manifests, then resolve them to get compliant properties.
+
+## Discovery
+
+| Task | Purpose | Response Time |
+|------|---------|---------------|
+| [list_property_features](/docs/governance/property/tasks/list_property_features) | Discover agent capabilities | ~200ms |
+
+## Property List Management
+
+| Task | Purpose | Response Time |
+|------|---------|---------------|
+| [create_property_list](/docs/governance/property/tasks/property_lists#create_property_list) | Create a new property list | ~500ms |
+| [update_property_list](/docs/governance/property/tasks/property_lists#update_property_list) | Modify an existing list | ~500ms |
+| [get_property_list](/docs/governance/property/tasks/property_lists#get_property_list) | Retrieve list with resolved properties | ~2-5s |
+| [list_property_lists](/docs/governance/property/tasks/property_lists#list_property_lists) | List all property lists | ~500ms |
+| [delete_property_list](/docs/governance/property/tasks/property_lists#delete_property_list) | Delete a property list | ~200ms |
+
+See [Property List Management](/docs/governance/property/tasks/property_lists) for complete CRUD documentation.
+
+## Validation
+
+| Task | Purpose | Response Time |
+|------|---------|---------------|
+| [validate_property_delivery](/docs/governance/property/tasks/validate_property_delivery) | Validate delivery records against a list | ~1-5s |
+
+See [validate_property_delivery](/docs/governance/property/tasks/validate_property_delivery) for post-campaign compliance validation.
+
+## Task Selection Guide
+
+### Creating a Property List
+
+Use `create_property_list` with filters and brand manifest:
+
+```json
+{
+ "tool": "create_property_list",
+ "arguments": {
+ "name": "Q1 Campaign - UK Premium",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news"]
+ }
+ ],
+ "filters": {
+ "countries_all": ["UK"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ {
+ "feature_id": "consent_quality",
+ "min_value": 85,
+ "max_value": 100
+ },
+ {
+ "feature_id": "coppa_certified",
+ "allowed_values": [true]
+ }
+ ]
+ },
+ "brand_manifest": {
+ "name": "ToyBrand",
+ "industry": "toys",
+ "target_audience": "children_under_13"
+ }
+ }
+}
+```
+
+**Required filters**: At least one country in `countries_all` and one channel in `channels_any` must be provided.
+
+**Base properties**: An array of property sources to evaluate. Each entry is a discriminated union with `selection_type`:
+- **`publisher_tags`**: `{ "selection_type": "publisher_tags", "publisher_domain": "...", "tags": [...] }`
+- **`publisher_ids`**: `{ "selection_type": "publisher_ids", "publisher_domain": "...", "property_ids": [...] }`
+- **`identifiers`**: `{ "selection_type": "identifiers", "identifiers": [...] }`
+- **Omitted**: Query the agent's entire property database
+
+**Filter logic** (explicit in field names):
+- `countries_all`: Property must have feature data for ALL listed countries
+- `channels_any`: Property must support ANY of the listed channels
+- `feature_requirements`: Property must pass ALL requirements (AND)
+
+Filters have two built-in fields (`countries_all`, `channels_any`) plus `feature_requirements` which reference features the agent provides (discovered via `list_property_features`). For quantitative features, use `min_value`/`max_value`. For binary or categorical features, use `allowed_values`.
+
+### Getting Resolved Properties
+
+Use `get_property_list` to retrieve the list with resolved identifiers:
+
+```json
+{
+ "tool": "get_property_list",
+ "arguments": {
+ "list_id": "pl_abc123",
+ "resolve": true
+ }
+}
+```
+
+Response includes resolved identifiers. Note that **raw scores are not returned** - only identifiers that pass the filter thresholds are included:
+
+```json
+{
+ "list_id": "pl_abc123",
+ "identifiers": [
+ { "type": "domain", "value": "bbc.co.uk" },
+ { "type": "domain", "value": "news.sky.com" }
+ ],
+ "cache_valid_until": "2026-01-04T17:15:00Z"
+}
+```
+
+The `auth_token` for sharing with sellers is returned at creation time (from `create_property_list`). Store it securely - it's only returned once.
+
+### Multi-Agent Integration
+
+Create the same property list on multiple governance agents, then configure webhooks to aggregate results:
+
+```python
+# Create lists on specialized agents
+consent_list = consent_agent.create_property_list(
+ name="Q1 - Consent",
+ base_properties=master_list,
+ brand_manifest=brand_manifest
+)
+consent_agent.update_property_list(
+ list_id=consent_list.list_id,
+ webhook_url="https://buyer.example.com/webhooks/consent"
+)
+
+scope3_list = scope3_agent.create_property_list(
+ name="Q1 - Sustainability",
+ base_properties=master_list,
+ brand_manifest=brand_manifest
+)
+scope3_agent.update_property_list(
+ list_id=scope3_list.list_id,
+ webhook_url="https://buyer.example.com/webhooks/scope3"
+)
+
+# Buyer agent intersects results when webhooks fire
+```
diff --git a/docs/governance/property/tasks/list_property_features.mdx b/docs/governance/property/tasks/list_property_features.mdx
new file mode 100644
index 000000000..6c7dcd16a
--- /dev/null
+++ b/docs/governance/property/tasks/list_property_features.mdx
@@ -0,0 +1,235 @@
+---
+sidebar_position: 1
+title: list_property_features
+---
+
+
+**AdCP 3.0 Proposal** - This task is under development for AdCP 3.0.
+
+
+**Task**: Discover what features a property governance agent can evaluate.
+
+**Response Time**: ~200ms
+
+**Request Schema**: [`https://adcontextprotocol.org/schemas/v1/governance/list-property-features-request.json`](https://adcontextprotocol.org/schemas/v1/governance/list-property-features-request.json)
+**Response Schema**: [`https://adcontextprotocol.org/schemas/v1/governance/list-property-features-response.json`](https://adcontextprotocol.org/schemas/v1/governance/list-property-features-response.json)
+
+The `list_property_features` task returns the features a governance agent can evaluate. This is the discovery mechanism for understanding an agent's capabilities before using other governance tasks.
+
+## Request Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `property_types` | string[] | No | Filter to features available for these property types |
+| `countries` | string[] | No | Filter to features available in these countries |
+
+## Response Structure
+
+All AdCP responses include:
+- **message**: Human-readable summary of the operation result
+- **context_id**: Session continuity identifier for follow-up requests
+- **data**: Task-specific payload (see Response Data below)
+
+## Response Data
+
+```json
+{
+ "features": [
+ {
+ "feature_id": "consent_quality",
+ "name": "Consent Quality Score",
+ "description": "Measures the quality of consent implementation including UX, granularity, and compliance",
+ "type": "quantitative",
+ "range": { "min": 0, "max": 100 },
+ "coverage": {
+ "property_types": ["website", "mobile_app"],
+ "countries": []
+ }
+ },
+ {
+ "feature_id": "coppa_certified",
+ "name": "COPPA Certified",
+ "description": "Whether the property has COPPA certification for child-directed content",
+ "type": "binary",
+ "coverage": {
+ "property_types": ["website", "mobile_app", "ctv_app"],
+ "countries": ["US"]
+ }
+ },
+ {
+ "feature_id": "content_category",
+ "name": "Content Category",
+ "description": "IAB content category classification",
+ "type": "categorical",
+ "allowed_values": ["news", "sports", "entertainment", "kids_family", "technology", "business", "adult", "violence"]
+ }
+ ]
+}
+```
+
+### Feature Types
+
+| Type | Description | Schema Fields |
+|------|-------------|---------------|
+| `binary` | True/false values | None additional |
+| `quantitative` | Numeric values within a range | `range: { min, max }` |
+| `categorical` | Enumerated string values | `allowed_values: string[]` |
+
+### Coverage
+
+The `coverage` object indicates where a feature applies:
+- **property_types**: Empty array means all property types
+- **countries**: Empty array means all countries
+
+## Protocol-Specific Examples
+
+### MCP Request
+
+```json
+{
+ "tool": "list_property_features",
+ "arguments": {}
+}
+```
+
+### MCP Response
+
+```json
+{
+ "message": "This agent evaluates 12 features across consent, brand safety, and sustainability.",
+ "context_id": "ctx-gov-features-123",
+ "features": [
+ {
+ "feature_id": "consent_quality",
+ "name": "Consent Quality Score",
+ "description": "Measures the quality of consent implementation",
+ "type": "quantitative",
+ "range": { "min": 0, "max": 100 }
+ },
+ {
+ "feature_id": "tcf_version",
+ "name": "TCF Version",
+ "description": "IAB Transparency & Consent Framework version supported",
+ "type": "categorical",
+ "allowed_values": ["none", "tcf_1.1", "tcf_2.0", "tcf_2.2"]
+ },
+ {
+ "feature_id": "coppa_certified",
+ "name": "COPPA Certified",
+ "description": "Whether property has COPPA certification",
+ "type": "binary",
+ "coverage": {
+ "property_types": ["website", "mobile_app", "ctv_app"],
+ "countries": ["US"]
+ }
+ }
+ ]
+}
+```
+
+### MCP Request - Filtered by Property Type
+
+```json
+{
+ "tool": "list_property_features",
+ "arguments": {
+ "property_types": ["mobile_app"]
+ }
+}
+```
+
+### A2A Request
+
+```javascript
+await a2a.send({
+ message: {
+ parts: [{
+ kind: "data",
+ data: {
+ skill: "list_property_features",
+ parameters: {}
+ }
+ }]
+ }
+});
+```
+
+## Example Feature Sets by Agent Type
+
+### Consent/Compliance Agent
+
+```json
+{
+ "features": [
+ { "feature_id": "consent_quality", "type": "quantitative", "range": { "min": 0, "max": 100 } },
+ { "feature_id": "tcf_version", "type": "categorical", "allowed_values": ["none", "tcf_2.0", "tcf_2.2"] },
+ { "feature_id": "coppa_certified", "type": "binary" },
+ { "feature_id": "gpp_supported", "type": "binary" },
+ { "feature_id": "vendor_count", "type": "quantitative", "range": { "min": 0, "max": 1000 } }
+ ]
+}
+```
+
+### Sustainability Agent (e.g., Scope3)
+
+```json
+{
+ "features": [
+ { "feature_id": "carbon_score", "type": "quantitative", "range": { "min": 0, "max": 100 } },
+ { "feature_id": "green_certified", "type": "binary" },
+ { "feature_id": "renewable_hosting", "type": "binary" },
+ { "feature_id": "emissions_per_impression", "type": "quantitative", "range": { "min": 0, "max": 100 } }
+ ]
+}
+```
+
+### Brand Safety Agent
+
+```json
+{
+ "features": [
+ { "feature_id": "content_category", "type": "categorical", "allowed_values": ["news", "sports", ...] },
+ { "feature_id": "brand_risk_score", "type": "quantitative", "range": { "min": 0, "max": 100 } },
+ { "feature_id": "sentiment", "type": "categorical", "allowed_values": ["positive", "neutral", "negative"] },
+ { "feature_id": "made_for_advertising", "type": "binary" }
+ ]
+}
+```
+
+## Integration Pattern
+
+Use `list_property_features` to understand agent capabilities before creating property lists:
+
+```python
+# Step 1: Discover agent capabilities
+features_response = governance_agent.list_property_features()
+
+# Step 2: Build feature_requirements using available features
+feature_requirements = []
+for feature in features_response.features:
+ if feature.feature_id == "consent_quality" and feature.type == "quantitative":
+ feature_requirements.append({
+ "feature_id": "consent_quality",
+ "min_value": 80,
+ "max_value": 100
+ })
+ if feature.feature_id == "coppa_certified" and feature.type == "binary":
+ feature_requirements.append({
+ "feature_id": "coppa_certified",
+ "allowed_values": [True]
+ })
+
+# Step 3: Create property list with feature requirements
+governance_agent.create_property_list(
+ name="Q1 Campaign",
+ filters={"feature_requirements": feature_requirements},
+ brand_manifest=my_brand_manifest
+)
+```
+
+## Usage Notes
+
+1. **Call first**: Use this task to discover capabilities before other governance tasks
+2. **Cache results**: Feature definitions change infrequently; cache for hours/days
+3. **Check coverage**: Not all features apply to all property types or countries
+4. **Marketplace discovery**: Use this to understand what different governance agents offer
diff --git a/docs/governance/property/tasks/property_lists.mdx b/docs/governance/property/tasks/property_lists.mdx
new file mode 100644
index 000000000..a46e54545
--- /dev/null
+++ b/docs/governance/property/tasks/property_lists.mdx
@@ -0,0 +1,701 @@
+---
+sidebar_position: 5
+title: Property List Management
+---
+
+
+**AdCP 3.0 Proposal** - These tasks are under development for AdCP 3.0.
+
+
+**Tasks**: Create, update, get, list, and delete property lists.
+
+Property lists are managed resources that combine static property sets with dynamic filters. When resolved, filters are applied to produce the final property set.
+
+## Architecture: Setup Time, Not Real-Time
+
+Property lists are designed for **setup-time** operations, not real-time bid decisions:
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│ SETUP TIME (Campaign Planning) │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ 1. Buyer creates/updates property list on governance agent │
+│ 2. Buyer resolves list to get current properties │
+│ 3. Buyer provides list_id to orchestrator/seller │
+│ 4. Orchestrator/seller fetches and caches resolved list │
+│ 5. Campaign targets only cached compliant properties │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+
+┌─────────────────────────────────────────────────────────────────┐
+│ BID TIME (Milliseconds) │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ • Orchestrator/seller uses LOCAL cache only │
+│ • NO calls to governance agent │
+│ • Pass/fail from cached property set │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+
+┌─────────────────────────────────────────────────────────────────┐
+│ REFRESH (Periodic) │
+├─────────────────────────────────────────────────────────────────┤
+│ │
+│ • Orchestrator/seller re-fetches list on schedule │
+│ • Frequency based on cache_valid_until │
+│ • Typically every 1-24 hours │
+│ │
+└─────────────────────────────────────────────────────────────────┘
+```
+
+This enables:
+
+- **Static lists**: Curated sets of approved properties
+- **Dynamic lists**: Properties matching criteria (country, channel, score thresholds)
+- **Hybrid lists**: Base set modified by filters
+
+## Tasks Overview
+
+| Task | Purpose | Response Time |
+|------|---------|---------------|
+| `create_property_list` | Create a new property list | ~500ms |
+| `update_property_list` | Modify an existing list | ~500ms |
+| `get_property_list` | Retrieve list with resolved properties | ~2-5s (depending on size) |
+| `list_property_lists` | List all property lists | ~500ms |
+| `delete_property_list` | Delete a property list | ~200ms |
+
+## Property List Structure
+
+A property list contains:
+
+```json
+{
+ "list_id": "uk_premium_news_q1",
+ "name": "UK Premium News Sites Q1 2026",
+ "description": "High-quality UK news sites for Q1 campaign",
+ "principal": "did:principal:brand-x",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news", "uk_tier1"]
+ }
+ ],
+ "filters": {
+ "countries_all": ["UK"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ { "feature_id": "consent_quality", "min_value": 90, "max_value": 100 }
+ ]
+ },
+ "brand_manifest": {
+ "industry": "retail",
+ "target_audience": "general"
+ },
+ "created_at": "2026-01-03T10:00:00Z",
+ "updated_at": "2026-01-03T10:00:00Z",
+ "property_count": 847
+}
+```
+
+## Brand Manifest
+
+Instead of manually specifying all filters, provide a brand manifest and let the governance agent apply appropriate rules based on who you are:
+
+```json
+{
+ "brand_manifest": {
+ "name": "ToyBrand",
+ "industry": "toys",
+ "target_audience": "children under 13",
+ "tone": "playful and safe"
+ }
+}
+```
+
+The agent interprets the manifest based on its domain expertise:
+- A consent agent applies COPPA requirements based on `target_audience`
+- A brand safety agent infers content categories from `industry`
+- A sustainability agent applies requirements from the brand's profile
+
+The brand manifest uses the standard [core/brand-manifest](https://adcontextprotocol.org/schemas/v2/core/brand-manifest.json) schema which includes fields for `industry`, `target_audience`, `tone`, and other brand identity information.
+
+## Webhooks
+
+Configure webhooks via `update_property_list` to receive notifications when the resolved list changes.
+
+**Important**: Webhooks provide **notification only**. They tell you that the list has changed, but do not stream the updated properties. After receiving a webhook, you must call `get_property_list` to retrieve the updated property set.
+
+### Webhook Flow
+
+```
+1. Governance agent re-evaluates properties (periodically or on trigger)
+2. Agent detects changes to the resolved property list
+3. Webhook fires with change summary (counts, not full list)
+4. Recipient calls get_property_list(list_id, resolve=true)
+5. Recipient updates local cache with new properties
+```
+
+### Webhook Payload
+
+```json
+{
+ "event": "property_list_changed",
+ "list_id": "uk_premium_news_q1",
+ "list_name": "UK Premium News Sites Q1 2026",
+ "change_summary": {
+ "properties_added": 12,
+ "properties_removed": 3,
+ "total_properties": 856
+ },
+ "resolved_at": "2026-01-03T18:00:00Z",
+ "cache_valid_until": "2026-01-04T18:00:00Z"
+}
+```
+
+The webhook payload includes counts but NOT the actual properties. This keeps payloads small and avoids redundant data transfer when recipients may not need the full list immediately.
+
+### Webhook Use Cases
+
+1. **Buyer agent aggregation**: Receive updates from multiple specialized agents, intersect results
+2. **Seller cache invalidation**: Know when to re-fetch the compliant property list
+3. **Alerting**: Notify when significant changes occur to compliance status
+
+## Filters
+
+Filters are applied when the list is resolved (via `get_property_list`):
+
+| Filter | Type | Description |
+|--------|------|-------------|
+| `countries_all` | string[] | ISO country codes - property must have feature data for ALL (required) |
+| `channels_any` | string[] | Advertising channels - property must support ANY (required) |
+| `property_types` | string[] | Property types (website, mobile_app, ctv_app, etc.) |
+| `feature_requirements` | FeatureRequirement[] | Requirements based on agent-provided features |
+| `exclude_identifiers` | Identifier[] | Identifiers to always exclude |
+
+### Feature Requirements
+
+Feature requirements reference features discovered via `list_property_features`. Each agent exposes different features (consent_quality, carbon_score, coppa_certified, etc.).
+
+For **quantitative** features (scores, ranges):
+```json
+{ "feature_id": "consent_quality", "min_value": 85, "max_value": 100 }
+```
+
+For **binary** features (true/false):
+```json
+{ "feature_id": "coppa_certified", "allowed_values": [true] }
+```
+
+For **categorical** features (enum values):
+```json
+{ "feature_id": "content_category", "allowed_values": ["news", "sports", "technology"] }
+```
+
+### Handling Missing Coverage
+
+When a property doesn't have data for a required feature, you can control the behavior with `if_not_covered`:
+
+```json
+{
+ "feature_id": "viewability_score",
+ "min_value": 70,
+ "if_not_covered": "include"
+}
+```
+
+| Value | Behavior | Use Case |
+|-------|----------|----------|
+| `exclude` (default) | Property is removed from the list | Strict enforcement - only include properties with verified data |
+| `include` | Property passes this requirement | Lenient enforcement - don't penalize for coverage gaps |
+
+When `if_not_covered: "include"` is used, the response includes a `coverage_gaps` field showing which properties were included despite missing data:
+
+```json
+{
+ "identifiers": [...],
+ "coverage_gaps": {
+ "viewability_score": [
+ { "type": "domain", "value": "app.example.com" },
+ { "type": "domain", "value": "ctv.example.com" }
+ ]
+ }
+}
+```
+
+This transparency helps agencies distinguish between properties that passed a requirement vs. those that couldn't be evaluated.
+
+### Required Filters
+
+Every property list must include at least:
+- One country in `countries_all` (ISO country code)
+- One channel in `channels_any` (display, video, audio, etc.)
+
+These are required because governance agents need to know which jurisdiction and context to evaluate properties against.
+
+### Filter Logic
+
+The filter field names make the logic explicit:
+
+- **`countries_all`**: Property must have feature data for **ALL** listed countries.
+- **`channels_any`**: Property must support **ANY** of the listed channels.
+- **`feature_requirements`**: Property must pass **ALL** requirements (AND).
+
+### Base Properties
+
+`base_properties` is an array of property sources to evaluate. Each entry is a **discriminated union** with `selection_type` as the discriminator:
+
+```json
+{
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news", "tier1"]
+ },
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "mediavine.com",
+ "tags": ["lifestyle"]
+ },
+ {
+ "selection_type": "identifiers",
+ "identifiers": [
+ { "type": "domain", "value": "bbc.co.uk" },
+ { "type": "domain", "value": "ft.com" }
+ ]
+ }
+ ]
+}
+```
+
+Each entry must include `selection_type`:
+
+| selection_type | Required Fields | Description |
+|-------------|-----------------|-------------|
+| `publisher_tags` | `publisher_domain`, `tags` | All properties matching these tags within the publisher |
+| `publisher_ids` | `publisher_domain`, `property_ids` | Specific property IDs within the publisher |
+| `identifiers` | `identifiers` | Direct domain/app identifiers (no publisher context) |
+
+If `base_properties` is omitted, the agent queries its entire property database for properties matching the filters.
+
+See the [base-property-source schema](https://adcontextprotocol.org/schemas/v2/property/base-property-source.json) for the full specification.
+
+---
+
+## create_property_list
+
+Create a new property list.
+
+### Request
+
+```json
+{
+ "tool": "create_property_list",
+ "arguments": {
+ "name": "UK Premium News Q1",
+ "description": "High-quality UK news sites for Q1 campaign",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news"]
+ }
+ ],
+ "filters": {
+ "countries_all": ["UK"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ { "feature_id": "consent_quality", "min_value": 85, "max_value": 100 }
+ ]
+ }
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "message": "Created property list 'UK Premium News Q1'.",
+ "context_id": "ctx-gov-list-123",
+ "list": {
+ "list_id": "pl_abc123",
+ "name": "UK Premium News Q1",
+ "description": "High-quality UK news sites for Q1 campaign",
+ "principal": "did:principal:brand-x",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news"]
+ }
+ ],
+ "filters": {
+ "countries_all": ["UK"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ { "feature_id": "consent_quality", "min_value": 85, "max_value": 100 }
+ ]
+ },
+ "created_at": "2026-01-03T16:30:00Z",
+ "updated_at": "2026-01-03T16:30:00Z",
+ "property_count": 847
+ },
+ "auth_token": "eyJhbGciOiJIUzI1NiIs..."
+}
+```
+
+### Dynamic List (Filters Only)
+
+Create a list that dynamically queries the governance agent's database (no base_properties - uses agent's full coverage):
+
+```json
+{
+ "tool": "create_property_list",
+ "arguments": {
+ "name": "GDPR-Compliant DE Video",
+ "description": "All DE properties supporting video with strong consent",
+ "filters": {
+ "countries_all": ["DE"],
+ "channels_any": ["video"],
+ "feature_requirements": [
+ { "feature_id": "consent_quality", "min_value": 90, "max_value": 100 }
+ ]
+ }
+ }
+}
+```
+
+---
+
+## update_property_list
+
+Modify an existing property list.
+
+### Request - Update Filters
+
+```json
+{
+ "tool": "update_property_list",
+ "arguments": {
+ "list_id": "pl_abc123",
+ "filters": {
+ "countries_all": ["UK"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ { "feature_id": "consent_quality", "min_value": 80, "max_value": 100 }
+ ]
+ }
+ }
+}
+```
+
+### Request - Replace Base Properties
+
+```json
+{
+ "tool": "update_property_list",
+ "arguments": {
+ "list_id": "pl_abc123",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news", "uk_tier1"]
+ }
+ ]
+ }
+}
+```
+
+### Request - Add Exclusions
+
+```json
+{
+ "tool": "update_property_list",
+ "arguments": {
+ "list_id": "pl_abc123",
+ "filters": {
+ "exclude_identifiers": [
+ { "type": "domain", "value": "excluded-site.com" }
+ ]
+ }
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "message": "Updated property list 'UK Premium News Q1'.",
+ "context_id": "ctx-gov-list-456",
+ "list": {
+ "list_id": "pl_abc123",
+ "name": "UK Premium News Q1",
+ "updated_at": "2026-01-03T17:00:00Z",
+ "property_count": 845
+ }
+}
+```
+
+---
+
+## get_property_list
+
+Retrieve a property list with optional resolution of filters.
+
+### Request - Get Resolved Properties
+
+```json
+{
+ "tool": "get_property_list",
+ "arguments": {
+ "list_id": "pl_abc123",
+ "resolve": true,
+ "max_results": 100
+ }
+}
+```
+
+### Response
+
+The response returns a compact list of **identifiers only** (not full property objects) for efficiency. Only identifiers that pass the feature requirements are included - no scores or metadata.
+
+```json
+{
+ "message": "Retrieved property list 'UK Premium News Q1' with 847 resolved identifiers.",
+ "context_id": "ctx-gov-list-789",
+ "list_id": "pl_abc123",
+ "identifiers": [
+ { "type": "domain", "value": "bbc.co.uk" },
+ { "type": "domain", "value": "news.sky.com" },
+ { "type": "domain", "value": "ft.com" },
+ { "type": "domain", "value": "theguardian.com" }
+ ],
+ "total_count": 847,
+ "returned_count": 100,
+ "pagination": {
+ "has_more": true,
+ "cursor": "eyJvZmZzZXQiOjEwMH0="
+ },
+ "resolved_at": "2026-01-03T17:15:00Z",
+ "cache_valid_until": "2026-01-04T17:15:00Z"
+}
+```
+
+
+The `auth_token` is only returned when the list is created via `create_property_list`. Store it securely - you'll need it to share access with sellers.
+
+
+### Request - Get Metadata Only
+
+```json
+{
+ "tool": "get_property_list",
+ "arguments": {
+ "list_id": "pl_abc123",
+ "resolve": false
+ }
+}
+```
+
+### Response (Metadata Only)
+
+```json
+{
+ "message": "Retrieved property list 'UK Premium News Q1' metadata.",
+ "context_id": "ctx-gov-list-790",
+ "list": {
+ "list_id": "pl_abc123",
+ "name": "UK Premium News Q1",
+ "description": "High-quality UK news sites for Q1 campaign",
+ "base_properties": [
+ {
+ "selection_type": "publisher_tags",
+ "publisher_domain": "raptive.com",
+ "tags": ["premium_news"]
+ }
+ ],
+ "filters": {
+ "countries_all": ["UK"],
+ "channels_any": ["display", "video"],
+ "feature_requirements": [
+ { "feature_id": "consent_quality", "min_value": 85, "max_value": 100 }
+ ]
+ },
+ "created_at": "2026-01-03T16:30:00Z",
+ "updated_at": "2026-01-03T17:00:00Z",
+ "property_count": 847
+ }
+}
+```
+
+---
+
+## list_property_lists
+
+List all property lists accessible to the authenticated principal.
+
+### Request
+
+```json
+{
+ "tool": "list_property_lists",
+ "arguments": {
+ "name_contains": "UK",
+ "max_results": 50
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "message": "Found 3 property lists matching 'UK'.",
+ "context_id": "ctx-gov-list-list-123",
+ "lists": [
+ {
+ "list_id": "pl_abc123",
+ "name": "UK Premium News Q1",
+ "description": "High-quality UK news sites for Q1 campaign",
+ "created_at": "2026-01-03T16:30:00Z",
+ "updated_at": "2026-01-03T17:00:00Z",
+ "property_count": 847
+ },
+ {
+ "list_id": "pl_def456",
+ "name": "UK Sports Sites",
+ "description": "UK sports content for sponsorship",
+ "created_at": "2026-01-02T10:00:00Z",
+ "updated_at": "2026-01-02T10:00:00Z",
+ "property_count": 156
+ }
+ ],
+ "total_count": 3,
+ "returned_count": 3,
+ "pagination": {
+ "has_more": false
+ }
+}
+```
+
+---
+
+## delete_property_list
+
+Delete a property list.
+
+### Request
+
+```json
+{
+ "tool": "delete_property_list",
+ "arguments": {
+ "list_id": "pl_abc123"
+ }
+}
+```
+
+### Response
+
+```json
+{
+ "message": "Deleted property list 'UK Premium News Q1'.",
+ "context_id": "ctx-gov-list-del-123",
+ "deleted": true,
+ "list_id": "pl_abc123"
+}
+```
+
+---
+
+## Integration with Other Tasks
+
+### Using Lists in score_properties
+
+Reference a property list instead of passing properties inline:
+
+```json
+{
+ "tool": "score_properties",
+ "arguments": {
+ "property_list_ref": {
+ "agent_url": "https://governance.example.com",
+ "list_id": "pl_abc123"
+ },
+ "scoring_context": {
+ "jurisdiction": "GDPR"
+ }
+ }
+}
+```
+
+### Using Lists in Media Buys
+
+Pass property lists to media buy creation:
+
+```json
+{
+ "tool": "create_media_buy",
+ "arguments": {
+ "packages": [{
+ "property_list_ref": {
+ "agent_url": "https://governance.example.com",
+ "list_id": "pl_abc123"
+ }
+ }]
+ }
+}
+```
+
+---
+
+## Error Codes
+
+| Code | Description |
+|------|-------------|
+| `LIST_NOT_FOUND` | Property list ID doesn't exist |
+| `LIST_ACCESS_DENIED` | Principal doesn't have access to this list |
+| `INVALID_FILTER` | Filter configuration is invalid |
+| `LIST_NAME_EXISTS` | A list with this name already exists |
+
+## Caching and Refresh
+
+The `get_property_list` response includes caching guidance:
+
+| Field | Description |
+|-------|-------------|
+| `resolved_at` | When filters were applied and properties resolved |
+| `cache_valid_until` | When consumers should re-fetch the list |
+
+**Typical flow for orchestrators/sellers:**
+
+```python
+# Initial setup
+response = governance_agent.get_property_list(list_id, resolve=True)
+local_cache = build_property_lookup(response.properties)
+cache_expiry = response.cache_valid_until
+
+# Periodic refresh (background job)
+if now() >= cache_expiry:
+ response = governance_agent.get_property_list(list_id, resolve=True)
+ local_cache = build_property_lookup(response.properties)
+ cache_expiry = response.cache_valid_until
+
+# Bid time (no external calls)
+def should_bid(property_domain):
+ return property_domain in local_cache
+```
+
+## Usage Notes
+
+1. **Setup time only**: Governance agents are not in the real-time bid path; resolve lists during campaign setup
+2. **Local caching**: Orchestrators/sellers must cache resolved properties locally for bid-time decisions
+3. **Refresh on schedule**: Re-fetch lists based on `cache_valid_until` (typically every 1-24 hours)
+4. **Dynamic vs Static**: Use filters-only lists when you want the agent to maintain the property set; use base_properties when you need explicit control
+5. **Pagination**: Large lists may require multiple requests with cursor-based pagination
+6. **No score leakage**: Raw scores are kept internal to governance agents; responses contain pass/fail lists, not scores
diff --git a/docs/governance/property/tasks/validate_property_delivery.mdx b/docs/governance/property/tasks/validate_property_delivery.mdx
new file mode 100644
index 000000000..7f9acc8c4
--- /dev/null
+++ b/docs/governance/property/tasks/validate_property_delivery.mdx
@@ -0,0 +1,402 @@
+---
+sidebar_position: 4
+title: validate_property_delivery
+---
+
+# validate_property_delivery
+
+
+**AdCP 3.0 Proposal** - This task is under development for AdCP 3.0.
+
+
+Validates delivery records against a property list to determine compliance. Answers two questions:
+1. **Property compliance**: Did my impressions land on properties in my list?
+2. **Supply path authorization**: Was the sales agent authorized to sell that inventory?
+
+## Use Cases
+
+- **Post-campaign validation**: Verify that impressions were delivered to compliant properties
+- **Supply path verification**: Confirm sales agents were authorized by publishers
+- **Real-time monitoring**: Check compliance rate during campaign execution
+- **Audit trails**: Generate compliance reports for regulatory or brand safety reviews
+
+## Request
+
+```json
+{
+ "$schema": "/schemas/property/validate-property-delivery-request.json",
+ "list_id": "pl_abc123",
+ "records": [
+ {
+ "identifier": { "type": "domain", "value": "www.nytimes.com" },
+ "impressions": 103
+ },
+ {
+ "identifier": { "type": "domain", "value": "sketchy-site.example" },
+ "impressions": 47
+ },
+ {
+ "identifier": { "type": "android_package", "value": "com.unknown.app" },
+ "impressions": 25
+ }
+ ],
+ "include_compliant": false
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `list_id` | string | Yes | ID of the property list to validate against |
+| `records` | array | Yes | Delivery records to validate (1-10,000 records) |
+| `records[].identifier` | object | Yes | Property identifier (`type` and `value`) |
+| `records[].impressions` | integer | Yes | Number of impressions delivered |
+| `records[].record_id` | string | No | Client-provided ID for correlation |
+| `records[].sales_agent_url` | string | No | Sales agent URL to validate authorization against adagents.json |
+| `include_compliant` | boolean | No | Include compliant records in results (default: false) |
+
+## Response
+
+```json
+{
+ "$schema": "/schemas/property/validate-property-delivery-response.json",
+ "list_id": "pl_abc123",
+ "summary": {
+ "total_records": 4,
+ "total_impressions": 200,
+ "compliant_records": 1,
+ "compliant_impressions": 103,
+ "non_compliant_records": 1,
+ "non_compliant_impressions": 47,
+ "not_covered_records": 1,
+ "not_covered_impressions": 25,
+ "unidentified_records": 1,
+ "unidentified_impressions": 25
+ },
+ "aggregate": {
+ "score": 68.7,
+ "grade": "C+",
+ "label": "68.7% compliant",
+ "methodology_url": "https://governance.example.com/methodology/compliance-scoring"
+ },
+ "results": [
+ {
+ "identifier": { "type": "domain", "value": "sketchy-site.example" },
+ "status": "non_compliant",
+ "impressions": 47,
+ "violations": [
+ {
+ "code": "not_in_list",
+ "message": "Identifier not found in resolved property list"
+ }
+ ]
+ },
+ {
+ "identifier": { "type": "domain", "value": "new-site.example" },
+ "status": "not_covered",
+ "impressions": 25
+ },
+ {
+ "identifier": { "type": "android_package", "value": "com.unknown.app" },
+ "status": "unidentified",
+ "impressions": 25
+ }
+ ],
+ "validated_at": "2026-01-04T19:00:00Z",
+ "list_resolved_at": "2026-01-04T12:00:00Z"
+}
+```
+
+### Response Fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `list_id` | string | ID of the property list validated against |
+| `summary` | object | Raw counts for property compliance validation |
+| `aggregate` | object | Optional computed metrics from the governance agent |
+| `results` | array | Per-record validation results |
+| `validated_at` | datetime | When validation was performed |
+| `list_resolved_at` | datetime | Resolution timestamp of the property list used |
+
+### Summary Fields
+
+The summary provides raw counts - consumers calculate rates as needed:
+
+| Field | Description |
+|-------|-------------|
+| `total_records` | Total records validated |
+| `total_impressions` | Total impressions across all records |
+| `compliant_records` / `compliant_impressions` | Records/impressions in the property list |
+| `non_compliant_records` / `non_compliant_impressions` | Records/impressions NOT in the property list |
+| `not_covered_records` / `not_covered_impressions` | Identifier recognized but no data available |
+| `unidentified_records` / `unidentified_impressions` | Identifier type not resolvable |
+
+### Validation Statuses
+
+| Status | Meaning |
+|--------|---------|
+| `compliant` | Identifier is in the resolved property list |
+| `non_compliant` | Identifier is NOT in the resolved property list |
+| `not_covered` | Identifier recognized but governance agent has no data for it |
+| `unidentified` | Identifier type not resolvable by this agent |
+
+## Understanding not_covered vs unidentified
+
+These two statuses distinguish different types of "unknown" scenarios:
+
+**`not_covered`** - The governance agent recognized the identifier (e.g., it's a valid domain) but doesn't have data for that specific property. This happens when:
+- A property is too new to be in the agent's database
+- The property exists but hasn't been evaluated yet
+- The agent's coverage doesn't include that property category
+
+**`unidentified`** - The governance agent couldn't recognize the identifier at all. This happens when:
+- Client-side detection failed to capture the property
+- The identifier type isn't supported (e.g., agent handles domains but received an app ID)
+- The identifier value is malformed or invalid
+
+Both statuses should be excluded from compliance rate calculations - you cannot penalize for detection or coverage gaps.
+
+## Optional Aggregate Metrics
+
+Governance agents can optionally return computed metrics in the `aggregate` field:
+
+```json
+"aggregate": {
+ "score": 68.7,
+ "grade": "C+",
+ "label": "68.7% compliant",
+ "methodology_url": "https://governance.example.com/methodology"
+}
+```
+
+| Field | Description |
+|-------|-------------|
+| `score` | Numeric score (scale is agent-defined, typically 0-100) |
+| `grade` | Letter grade or category (e.g., "A+", "B-", "Gold") |
+| `label` | Human-readable summary (e.g., "85% compliant") |
+| `methodology_url` | URL explaining how the aggregate was calculated |
+
+The `aggregate` field is optional and agent-specific. Consumers should not assume a particular format - always check `methodology_url` for interpretation.
+
+## Calculating Your Own Rates
+
+The response always includes raw counts. Calculate rates as needed:
+
+```python
+# Compliance rate (exclude unverifiable from denominator)
+unverifiable = summary.not_covered_impressions + summary.unidentified_impressions
+verifiable = summary.total_impressions - unverifiable
+compliance_rate = summary.compliant_impressions / verifiable if verifiable > 0 else None
+```
+
+In the example above:
+- Compliant impressions: 103
+- Non-compliant impressions: 47
+- not_covered + unidentified impressions: 50 (excluded)
+- Compliance rate: 103 / (200 - 50) = 103 / 150 = 68.7%
+
+## Violation Codes
+
+| Code | Description |
+|------|-------------|
+| `not_in_list` | Identifier not found in the resolved property list |
+| `excluded` | Identifier explicitly excluded via `exclude_identifiers` filter |
+| `country_mismatch` | Property lacks feature data for required countries |
+| `channel_mismatch` | Property doesn't support required channels |
+| `feature_failed` | Property failed a `feature_requirements` filter |
+
+### Feature Violation Details
+
+When a property fails a feature requirement, the violation includes the feature details:
+
+```json
+{
+ "identifier": { "type": "domain", "value": "low-quality-site.example" },
+ "status": "non_compliant",
+ "impressions": 150,
+ "violations": [
+ {
+ "code": "feature_failed",
+ "message": "Property failed consent_quality requirement (min: 85)",
+ "feature_id": "consent_quality",
+ "requirement": {
+ "min_value": 85
+ }
+ }
+ ]
+}
+```
+
+The `feature_id` and `requirement` fields are optional - they're included when the violation is due to a feature requirement filter.
+
+## Supply Path Authorization
+
+When `sales_agent_url` is provided in delivery records, the governance agent validates that the sales agent is authorized to sell the property by checking the publisher's `adagents.json`.
+
+### Request with Authorization
+
+```json
+{
+ "list_id": "pl_abc123",
+ "records": [
+ {
+ "identifier": { "type": "domain", "value": "www.nytimes.com" },
+ "impressions": 103,
+ "sales_agent_url": "https://legitimate-ssp.example.com"
+ },
+ {
+ "identifier": { "type": "domain", "value": "www.nytimes.com" },
+ "impressions": 50,
+ "sales_agent_url": "https://unauthorized-reseller.example.com"
+ }
+ ]
+}
+```
+
+### Response with Authorization
+
+When authorization is validated, each result includes an `authorization` field:
+
+```json
+{
+ "list_id": "pl_abc123",
+ "summary": {
+ "total_records": 2,
+ "total_impressions": 153,
+ "compliant_records": 2,
+ "compliant_impressions": 153,
+ "non_compliant_records": 0,
+ "non_compliant_impressions": 0,
+ "unknown_records": 0,
+ "unknown_impressions": 0
+ },
+ "authorization_summary": {
+ "records_checked": 2,
+ "impressions_checked": 153,
+ "authorized_records": 1,
+ "authorized_impressions": 103,
+ "unauthorized_records": 1,
+ "unauthorized_impressions": 50,
+ "unknown_records": 0,
+ "unknown_impressions": 0
+ },
+ "results": [
+ {
+ "identifier": { "type": "domain", "value": "www.nytimes.com" },
+ "status": "compliant",
+ "impressions": 50,
+ "authorization": {
+ "status": "unauthorized",
+ "publisher_domain": "nytimes.com",
+ "sales_agent_url": "https://unauthorized-reseller.example.com",
+ "violation": {
+ "code": "agent_not_authorized",
+ "message": "Sales agent not listed in nytimes.com/.well-known/adagents.json"
+ }
+ }
+ }
+ ],
+ "validated_at": "2026-01-04T19:00:00Z"
+}
+```
+
+### Authorization Statuses
+
+| Status | Meaning | Included in authorization_rate? |
+|--------|---------|--------------------------------|
+| `authorized` | Sales agent is listed in publisher's adagents.json | Yes (numerator) |
+| `unauthorized` | Sales agent is NOT listed in publisher's adagents.json | Yes (denominator only) |
+| `unknown` | Could not fetch or parse adagents.json | No (excluded) |
+
+### Authorization Violation Codes
+
+| Code | Description |
+|------|-------------|
+| `agent_not_authorized` | Sales agent URL not found in publisher's authorized_agents list |
+| `adagents_not_found` | Publisher's adagents.json could not be fetched (404, timeout, etc.) |
+| `adagents_invalid` | Publisher's adagents.json exists but is malformed |
+| `property_not_declared` | Property identifier not declared in publisher's adagents.json |
+
+### Two Independent Checks
+
+Property compliance and authorization are **independent checks**. A record can be:
+
+| Property Status | Authorization Status | Meaning |
+|-----------------|---------------------|---------|
+| compliant | authorized | Fully valid - property in list, sold by authorized agent |
+| compliant | unauthorized | Property is approved but sold by unauthorized reseller |
+| non_compliant | authorized | Authorized agent sold property outside your list |
+| non_compliant | unauthorized | Neither property nor agent validated |
+
+Both checks use the same "unknown excludes from rate" pattern - you cannot penalize for detection gaps.
+
+## Best Practices
+
+### Batch Validation
+
+For large-scale validation, batch records up to the 10,000 limit:
+
+```python
+def validate_delivery_batch(records, list_id, governance_agent):
+ """Validate delivery records in batches."""
+ batch_size = 10000
+ all_results = []
+
+ for i in range(0, len(records), batch_size):
+ batch = records[i:i + batch_size]
+ response = governance_agent.validate_property_delivery(
+ list_id=list_id,
+ records=batch
+ )
+ all_results.extend(response.results)
+
+ return all_results
+```
+
+### Sampling Strategy
+
+For real-time monitoring during campaign execution, validate a statistical sample rather than all records:
+
+```python
+import random
+
+def sample_and_validate(records, sample_size=1000):
+ """Validate a random sample for real-time monitoring."""
+ sample = random.sample(records, min(sample_size, len(records)))
+ return governance_agent.validate_property_delivery(
+ list_id=list_id,
+ records=sample
+ )
+```
+
+### Handling Unknown Records
+
+Track unknown rates separately to identify detection gaps:
+
+```python
+def analyze_validation(response):
+ """Analyze validation results with unknown handling."""
+ summary = response.summary
+
+ # Core compliance metric
+ compliance_rate = summary.compliance_rate
+
+ # Detection quality metric
+ unknown_rate = summary.unknown_impressions / summary.total_impressions
+
+ if unknown_rate > 0.1:
+ print(f"Warning: {unknown_rate:.1%} of impressions unresolvable")
+
+ return {
+ "compliance_rate": compliance_rate,
+ "unknown_rate": unknown_rate,
+ "non_compliant_impressions": summary.non_compliant_impressions
+ }
+```
+
+## Related Tasks
+
+- [create_property_list](/docs/governance/property/tasks/property_lists#create_property_list) - Create the list to validate against
+- [get_property_list](/docs/governance/property/tasks/property_lists#get_property_list) - Retrieve current list membership
+- [list_property_features](/docs/governance/property/tasks/list_property_features) - Discover available filter features
diff --git a/docs/media-buy/capability-discovery/index.mdx b/docs/media-buy/capability-discovery/index.mdx
index 885a0e16c..297984b4c 100644
--- a/docs/media-buy/capability-discovery/index.mdx
+++ b/docs/media-buy/capability-discovery/index.mdx
@@ -21,7 +21,7 @@ Learn how sales agents can support standard creative formats through the referen
- Leverage the Standard Creative Agent for standard formats
- Work with publisher-specific creative agents for custom formats
-### [Authorized Properties](/docs/media-buy/capability-discovery/authorized-properties) 🔐
+### [Understanding Authorization](/docs/governance/property/authorized-properties) 🔐
Learn how AdCP prevents unauthorized resale and ensures sales agents are legitimate. Understand:
- The problem of unauthorized resale in digital advertising
@@ -30,7 +30,7 @@ Learn how AdCP prevents unauthorized resale and ensures sales agents are legitim
- Property tags and large-scale authorization management
-**Cross-Protocol Foundation**: The `adagents.json` authorization system applies across ALL AdCP protocols (Media Buy, Signals, and future Curation). See the [adagents.json specification](/docs/media-buy/capability-discovery/adagents) for complete implementation details.
+**Cross-Protocol Foundation**: The `adagents.json` authorization system applies across ALL AdCP protocols (Media Buy, Signals, and future Curation). See the [adagents.json specification](/docs/governance/property/adagents) for complete implementation details.
@@ -80,4 +80,4 @@ Together, these capabilities provide the foundation for safe, efficient, and eff
- **[Creative Protocol](/docs/creative/)** - Creative agents and manifests
- **[Creative Channel Guides](/docs/creative/channels/video)** - Format examples and patterns
- **[Creative Manifests](/docs/creative/creative-manifests)** - Understanding creative specifications
-- **[adagents.json Specification](/docs/media-buy/capability-discovery/adagents)** - Publisher authorization system (applies across all AdCP protocols)
\ No newline at end of file
+- **[adagents.json Specification](/docs/governance/property/adagents)** - Publisher authorization system (applies across all AdCP protocols)
\ No newline at end of file
diff --git a/docs/media-buy/index.mdx b/docs/media-buy/index.mdx
index 0ead41042..ab06863c2 100644
--- a/docs/media-buy/index.mdx
+++ b/docs/media-buy/index.mdx
@@ -232,7 +232,7 @@ Choose your path based on your role and needs:
4. **Explore [Optimization & Reporting](/docs/media-buy/media-buys/optimization-reporting)** - Learn performance management
### **For Publishers/Sales Agents**
-1. **Learn [Authorized Properties](/docs/media-buy/capability-discovery/authorized-properties)** - Understand authorization requirements
+1. **Learn [Understanding Authorization](/docs/governance/property/authorized-properties)** - Understand authorization requirements
2. **Review [Creative Formats](/docs/creative/formats)** - See supported creative specifications
3. **Study [Advanced Topics](/docs/media-buy/advanced-topics/)** - Deep dive into technical implementation
diff --git a/docs/media-buy/task-reference/get_products.mdx b/docs/media-buy/task-reference/get_products.mdx
index 9a877d146..fa00a940b 100644
--- a/docs/media-buy/task-reference/get_products.mdx
+++ b/docs/media-buy/task-reference/get_products.mdx
@@ -129,6 +129,7 @@ asyncio.run(discover_with_filters())
| `brief` | string | No | Natural language description of campaign requirements |
| `brand_manifest` | BrandManifest \| string | No | Brand information (inline object or URL). See [Brand Manifest](/docs/creative/brand-manifest) |
| `filters` | Filters | No | Structured filters (see below) |
+| `property_list` | PropertyListRef | No | [AdCP 3.0] Reference to a property list for filtering. See [Property Lists](/docs/governance/property/tasks/property_lists) |
### Filters Object
@@ -172,6 +173,12 @@ Returns an array of `products`, each containing:
| `pricing_options` | PricingOption[] | Available pricing models (CPM, CPCV, etc.) |
| `brief_relevance` | string | Why this product matches the brief (when brief provided) |
+### Response Metadata
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `property_list_applied` | boolean | [AdCP 3.0] `true` if the agent filtered products based on the provided `property_list`. Absent or `false` if not provided or not supported. |
+
**See schema for complete field list**: [`get-products-response.json`](https://adcontextprotocol.org/schemas/v2/media-buy/get-products-response.json)
## Common Scenarios
@@ -476,6 +483,80 @@ asyncio.run(discover_standard_formats())
+### Property List Filtering
+
+
+**AdCP 3.0** - Property list filtering requires governance agent support.
+
+
+Filter products to only those available on properties in your approved list:
+
+
+
+```javascript JavaScript
+import { testAgent } from '@adcp/client/testing';
+
+// Filter products by property list from governance agent
+const result = await testAgent.getProducts({
+ brief: 'Brand-safe inventory for family brand',
+ brand_manifest: {
+ name: 'Nike',
+ url: 'https://nike.com'
+ },
+ property_list: {
+ agent_url: 'https://governance.example.com',
+ list_id: 'pl_brand_safe_2024'
+ }
+});
+
+if (result.success && result.data) {
+ // Check if filtering was actually applied
+ if (result.data.property_list_applied) {
+ console.log(`Found ${result.data.products.length} products on approved properties`);
+ } else {
+ console.log('Agent does not support property list filtering');
+ console.log(`Found ${result.data.products.length} products (unfiltered)`);
+ }
+}
+```
+
+```python Python
+import asyncio
+from adcp import test_agent
+
+async def discover_with_property_list():
+ # Filter products by property list from governance agent
+ result = await test_agent.simple.get_products(
+ brief='Brand-safe inventory for family brand',
+ brand_manifest={
+ 'name': 'Nike',
+ 'url': 'https://nike.com'
+ },
+ property_list={
+ 'agent_url': 'https://governance.example.com',
+ 'list_id': 'pl_brand_safe_2024'
+ }
+ )
+
+ # Check if filtering was actually applied
+ if result.get('property_list_applied'):
+ print(f"Found {len(result['products'])} products on approved properties")
+ else:
+ print("Agent does not support property list filtering")
+ print(f"Found {len(result['products'])} products (unfiltered)")
+
+asyncio.run(discover_with_property_list())
+```
+
+
+
+**Note**: If `property_list_applied` is absent or `false`, the sales agent did not filter products. This can happen if:
+- The agent doesn't support property governance features
+- The agent couldn't access the property list
+- The property list had no effect on the available inventory
+
+See [Property Governance](/docs/governance/property/specification) for more on property lists.
+
## Error Handling
| Error Code | Description | Resolution |
diff --git a/docs/media-buy/task-reference/list_authorized_properties.mdx b/docs/media-buy/task-reference/list_authorized_properties.mdx
index 5e7cb8ea9..55bd3fcf8 100644
--- a/docs/media-buy/task-reference/list_authorized_properties.mdx
+++ b/docs/media-buy/task-reference/list_authorized_properties.mdx
@@ -498,6 +498,6 @@ After discovering authorized properties:
## Learn More
-- [adagents.json Specification](/docs/media-buy/capability-discovery/adagents) - Publisher authorization file format
+- [adagents.json Specification](/docs/governance/property/adagents) - Publisher authorization file format
- [Property Schema](https://adcontextprotocol.org/schemas/v2/core/property.json) - Property definition structure
- [Authorization Guide](/docs/reference/authentication) - How authorization works in AdCP
diff --git a/docs/reference/implementor-faq.mdx b/docs/reference/implementor-faq.mdx
index ce93b224d..747ada9c0 100644
--- a/docs/reference/implementor-faq.mdx
+++ b/docs/reference/implementor-faq.mdx
@@ -289,7 +289,7 @@ assert(response.message.includes("policy"));
3. Verify the sales agent URL appears in `authorized_agents`
4. Reject products from unauthorized agents
-See [Authorization Validation](/docs/media-buy/capability-discovery/adagents#buyer-agent-validation) for complete requirements.
+See [Authorization Validation](/docs/governance/property/adagents#buyer-agent-validation) for complete requirements.
## Terminology
diff --git a/docs/reference/roadmap.mdx b/docs/reference/roadmap.mdx
index 35b1ec2d6..137ddeb29 100644
--- a/docs/reference/roadmap.mdx
+++ b/docs/reference/roadmap.mdx
@@ -59,9 +59,9 @@ These features are in active development but may not make the 3.0 release:
The following features are in the working group planning stages:
-### Governance Protocol
+### Governance Protocol [3.0]
-A framework for multi-party approval workflows and delegated authority management. See the [Governance Protocol Working Document](https://docs.google.com/document/d/1osokTr5Xk2PyLBUHbx2jpPad0TLrQIDh2I6ZWzpHMmY/edit?tab=t.0) for details.
+Property governance (property lists, filters, adagents.json authorization), brand standards (brand manifests, creative guidelines), and compliance controls. See the [Governance Protocol](/docs/governance/index) for current specification.
### Private Marketplace (PMP) Support
- Deal ID integration
diff --git a/package-lock.json b/package-lock.json
index e3bc17f29..6cb4764f1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "adcontextprotocol",
- "version": "2.5.2",
+ "version": "2.6.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "adcontextprotocol",
- "version": "2.5.2",
+ "version": "2.6.0",
"dependencies": {
"@adcp/client": "^3.5.2",
"@anthropic-ai/sdk": "^0.71.2",
diff --git a/static/schemas/source/adagents.json b/static/schemas/source/adagents.json
index 286aee996..bc48225ca 100644
--- a/static/schemas/source/adagents.json
+++ b/static/schemas/source/adagents.json
@@ -264,6 +264,38 @@
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp indicating when this file was last updated"
+ },
+ "property_features": {
+ "type": "array",
+ "description": "[AdCP 3.0] Optional list of agents that provide property feature data (certifications, scores, compliance status). Used for discovery - actual data comes from querying the agent's get_property_features task.",
+ "items": {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "description": "The agent's API endpoint URL (must implement get_property_features)"
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the vendor/agent (e.g., 'Scope3', 'TAG', 'OneTrust')"
+ },
+ "features": {
+ "type": "array",
+ "description": "Feature IDs this agent provides (e.g., 'carbon_score', 'tag_certified_against_fraud'). Use list_property_features on the agent for full definitions.",
+ "items": {
+ "type": "string"
+ },
+ "minItems": 1
+ },
+ "publisher_id": {
+ "type": "string",
+ "description": "Optional publisher identifier at this agent (for lookup)"
+ }
+ },
+ "required": ["url", "name", "features"],
+ "additionalProperties": true
+ }
}
},
"required": [
@@ -489,6 +521,66 @@
}
],
"last_updated": "2025-01-10T17:00:00Z"
+ },
+ {
+ "$schema": "/schemas/adagents.json",
+ "contact": {
+ "name": "Premium News Publisher",
+ "email": "adops@news.example.com",
+ "domain": "news.example.com"
+ },
+ "properties": [
+ {
+ "property_type": "website",
+ "name": "News Example",
+ "identifiers": [
+ {
+ "type": "domain",
+ "value": "news.example.com"
+ }
+ ],
+ "tags": ["premium", "news"],
+ "publisher_domain": "news.example.com"
+ }
+ ],
+ "tags": {
+ "premium": {
+ "name": "Premium Properties",
+ "description": "High-quality, brand-safe properties"
+ },
+ "news": {
+ "name": "News Properties",
+ "description": "News and journalism content"
+ }
+ },
+ "authorized_agents": [
+ {
+ "url": "https://sales.news.example.com",
+ "authorized_for": "All news properties",
+ "authorization_type": "property_tags",
+ "property_tags": ["news"]
+ }
+ ],
+ "property_features": [
+ {
+ "url": "https://api.scope3.com",
+ "name": "Scope3",
+ "features": ["carbon_score", "sustainability_grade"],
+ "publisher_id": "pub_news_12345"
+ },
+ {
+ "url": "https://api.tagtoday.net",
+ "name": "TAG",
+ "features": ["tag_certified_against_fraud", "tag_brand_safety_certified"]
+ },
+ {
+ "url": "https://api.onetrust.com",
+ "name": "OneTrust",
+ "features": ["gdpr_compliant", "tcf_registered", "ccpa_compliant"],
+ "publisher_id": "ot_news_67890"
+ }
+ ],
+ "last_updated": "2025-01-10T18:00:00Z"
}
]
}
diff --git a/static/schemas/source/core/identifier.json b/static/schemas/source/core/identifier.json
new file mode 100644
index 000000000..f7e68aabe
--- /dev/null
+++ b/static/schemas/source/core/identifier.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/core/identifier.json",
+ "title": "Identifier",
+ "description": "A property identifier with type and value. Used to identify properties across platforms (domains, app store IDs, etc.).",
+ "type": "object",
+ "properties": {
+ "type": {
+ "$ref": "/schemas/enums/identifier-types.json",
+ "description": "Type of identifier"
+ },
+ "value": {
+ "type": "string",
+ "description": "The identifier value. For domain type: 'example.com' matches base domain plus www and m subdomains; 'edition.example.com' matches that specific subdomain; '*.example.com' matches ALL subdomains but NOT base domain"
+ }
+ },
+ "required": ["type", "value"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/core/property-list-ref.json b/static/schemas/source/core/property-list-ref.json
new file mode 100644
index 000000000..06e5ced9b
--- /dev/null
+++ b/static/schemas/source/core/property-list-ref.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/core/property-list-ref.json",
+ "title": "Property List Reference",
+ "description": "Reference to an externally managed property list. Enables passing large property sets (50,000+) without embedding them in requests. The receiving agent fetches and caches the list independently.",
+ "type": "object",
+ "properties": {
+ "agent_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL of the agent managing the property list"
+ },
+ "list_id": {
+ "type": "string",
+ "description": "Identifier for the property list within the agent",
+ "minLength": 1
+ },
+ "auth_token": {
+ "type": "string",
+ "description": "JWT or other authorization token for accessing the list. Optional if the list is public or caller has implicit access."
+ }
+ },
+ "required": ["agent_url", "list_id"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/enums/adcp-domain.json b/static/schemas/source/enums/adcp-domain.json
index 68db23006..ee392aeff 100644
--- a/static/schemas/source/enums/adcp-domain.json
+++ b/static/schemas/source/enums/adcp-domain.json
@@ -4,8 +4,11 @@
"title": "AdCP Domain",
"description": "AdCP protocol domains for task categorization",
"type": "string",
- "enum": [
- "media-buy",
- "signals"
- ]
+ "enum": ["media-buy", "signals", "governance", "creative"],
+ "enumDescriptions": {
+ "media-buy": "Campaign creation, package management, and delivery optimization",
+ "signals": "Audience signal discovery and activation",
+ "governance": "Property governance (identity, authorization, data, selection), brand standards, and compliance",
+ "creative": "Creative asset management, format discovery, and rendering"
+ }
}
diff --git a/static/schemas/source/enums/task-type.json b/static/schemas/source/enums/task-type.json
index 063291bb4..7af80e9b9 100644
--- a/static/schemas/source/enums/task-type.json
+++ b/static/schemas/source/enums/task-type.json
@@ -9,18 +9,30 @@
"update_media_buy",
"sync_creatives",
"activate_signal",
- "get_signals"
+ "get_signals",
+ "list_property_features",
+ "create_property_list",
+ "update_property_list",
+ "get_property_list",
+ "list_property_lists",
+ "delete_property_list"
],
"enumDescriptions": {
"create_media_buy": "Media-buy domain: Create a new advertising campaign with one or more packages",
"update_media_buy": "Media-buy domain: Update campaign settings, package configuration, or delivery parameters",
"sync_creatives": "Media-buy domain: Sync creative assets to publisher's library with upsert semantics",
"activate_signal": "Signals domain: Activate an audience signal on a specific platform or account",
- "get_signals": "Signals domain: Discover available audience signals based on natural language description"
+ "get_signals": "Signals domain: Discover available audience signals based on natural language description",
+ "list_property_features": "Property domain: Discover what features a governance agent can evaluate",
+ "create_property_list": "Property domain: Create a new property list with filters and brand manifest",
+ "update_property_list": "Property domain: Update an existing property list",
+ "get_property_list": "Property domain: Retrieve a property list with resolved properties",
+ "list_property_lists": "Property domain: List all accessible property lists",
+ "delete_property_list": "Property domain: Delete a property list"
},
"notes": [
"Task types map to specific AdCP task operations",
- "Each task type belongs to either the 'media-buy' or 'signals' domain",
+ "Each task type belongs to the 'media-buy', 'signals', 'property', or 'creative' domain",
"This enum is used in task management APIs (tasks/list, tasks/get) and webhook payloads",
"New task types require a minor version bump per semantic versioning"
]
diff --git a/static/schemas/source/index.json b/static/schemas/source/index.json
index 6ea9e42a7..4ff0bab97 100644
--- a/static/schemas/source/index.json
+++ b/static/schemas/source/index.json
@@ -168,6 +168,14 @@
"property-tag": {
"$ref": "/schemas/core/property-tag.json",
"description": "Tag for categorizing publisher properties - lowercase alphanumeric with underscores only"
+ },
+ "property-list-ref": {
+ "$ref": "/schemas/core/property-list-ref.json",
+ "description": "Reference to an externally managed property list for passing large property sets"
+ },
+ "identifier": {
+ "$ref": "/schemas/core/identifier.json",
+ "description": "A property identifier with type and value"
}
}
},
@@ -220,7 +228,7 @@
},
"task-type": {
"$ref": "/schemas/enums/task-type.json",
- "description": "Valid AdCP task types across all domains (create_media_buy, update_media_buy, sync_creatives, activate_signal, get_signals)"
+ "description": "Valid AdCP task types across all domains (create_media_buy, update_media_buy, sync_creatives, activate_signal, get_signals, list_property_features, create_property_list, get_property_list, update_property_list, delete_property_list)"
},
"asset-content-type": {
"$ref": "/schemas/enums/asset-content-type.json",
@@ -280,7 +288,7 @@
},
"adcp-domain": {
"$ref": "/schemas/enums/adcp-domain.json",
- "description": "AdCP protocol domains (media-buy, signals)"
+ "description": "AdCP protocol domains (media-buy, signals, governance)"
},
"http-method": {
"$ref": "/schemas/enums/http-method.json",
@@ -562,6 +570,105 @@
}
}
},
+ "governance": {
+ "description": "Governance protocol for property governance, brand standards, and compliance",
+ "supporting-schemas": {
+ "property-feature-definition": {
+ "$ref": "/schemas/property/property-feature-definition.json",
+ "description": "Definition of a feature that a governance agent can evaluate"
+ },
+ "property-feature": {
+ "$ref": "/schemas/property/property-feature.json",
+ "description": "A discrete feature assessment for a property"
+ },
+ "feature-requirement": {
+ "$ref": "/schemas/property/feature-requirement.json",
+ "description": "A feature-based requirement for property filtering"
+ },
+ "property-error": {
+ "$ref": "/schemas/property/property-error.json",
+ "description": "Error information for a property that could not be evaluated"
+ },
+ "property-list": {
+ "$ref": "/schemas/property/property-list.json",
+ "description": "A managed property list with optional filters for dynamic evaluation"
+ },
+ "property-list-filters": {
+ "$ref": "/schemas/property/property-list-filters.json",
+ "description": "Filters that dynamically modify a property list when resolved"
+ },
+ "property-list-changed-webhook": {
+ "$ref": "/schemas/property/property-list-changed-webhook.json",
+ "description": "Webhook payload when a property list changes"
+ },
+ "base-property-source": {
+ "$ref": "/schemas/property/base-property-source.json",
+ "description": "A source of properties for a property list - supports publisher+tags, publisher+property_ids, or direct identifiers"
+ }
+ },
+ "tasks": {
+ "list-property-features": {
+ "request": {
+ "$ref": "/schemas/property/list-property-features-request.json",
+ "description": "Request parameters for discovering governance agent capabilities"
+ },
+ "response": {
+ "$ref": "/schemas/property/list-property-features-response.json",
+ "description": "Response payload for list_property_features task"
+ }
+ },
+ "create-property-list": {
+ "request": {
+ "$ref": "/schemas/property/create-property-list-request.json",
+ "description": "Request parameters for creating a new property list"
+ },
+ "response": {
+ "$ref": "/schemas/property/create-property-list-response.json",
+ "description": "Response payload for create_property_list task"
+ }
+ },
+ "update-property-list": {
+ "request": {
+ "$ref": "/schemas/property/update-property-list-request.json",
+ "description": "Request parameters for updating an existing property list"
+ },
+ "response": {
+ "$ref": "/schemas/property/update-property-list-response.json",
+ "description": "Response payload for update_property_list task"
+ }
+ },
+ "get-property-list": {
+ "request": {
+ "$ref": "/schemas/property/get-property-list-request.json",
+ "description": "Request parameters for retrieving a property list with resolved properties"
+ },
+ "response": {
+ "$ref": "/schemas/property/get-property-list-response.json",
+ "description": "Response payload for get_property_list task"
+ }
+ },
+ "list-property-lists": {
+ "request": {
+ "$ref": "/schemas/property/list-property-lists-request.json",
+ "description": "Request parameters for listing property lists"
+ },
+ "response": {
+ "$ref": "/schemas/property/list-property-lists-response.json",
+ "description": "Response payload for list_property_lists task"
+ }
+ },
+ "delete-property-list": {
+ "request": {
+ "$ref": "/schemas/property/delete-property-list-request.json",
+ "description": "Request parameters for deleting a property list"
+ },
+ "response": {
+ "$ref": "/schemas/property/delete-property-list-response.json",
+ "description": "Response payload for delete_property_list task"
+ }
+ }
+ }
+ },
"adagents": {
"description": "Authorized sales agents file format specification",
"$ref": "/schemas/adagents.json",
diff --git a/static/schemas/source/media-buy/get-products-request.json b/static/schemas/source/media-buy/get-products-request.json
index 1c7483fd8..02817c0f3 100644
--- a/static/schemas/source/media-buy/get-products-request.json
+++ b/static/schemas/source/media-buy/get-products-request.json
@@ -16,6 +16,10 @@
"filters": {
"$ref": "/schemas/core/product-filters.json"
},
+ "property_list": {
+ "$ref": "/schemas/core/property-list-ref.json",
+ "description": "[AdCP 3.0] Reference to an externally managed property list. When provided, the sales agent should filter products to only those available on properties in the list."
+ },
"context": {
"$ref": "/schemas/core/context.json"
},
diff --git a/static/schemas/source/media-buy/get-products-response.json b/static/schemas/source/media-buy/get-products-response.json
index 3a54bd3c8..692787b91 100644
--- a/static/schemas/source/media-buy/get-products-response.json
+++ b/static/schemas/source/media-buy/get-products-response.json
@@ -19,6 +19,10 @@
"$ref": "/schemas/core/error.json"
}
},
+ "property_list_applied": {
+ "type": "boolean",
+ "description": "[AdCP 3.0] Indicates whether property_list filtering was applied. True if the agent filtered products based on the provided property_list. Absent or false if property_list was not provided or not supported by this agent."
+ },
"context": {
"$ref": "/schemas/core/context.json"
},
diff --git a/static/schemas/source/property/authorization-result.json b/static/schemas/source/property/authorization-result.json
new file mode 100644
index 000000000..48187649f
--- /dev/null
+++ b/static/schemas/source/property/authorization-result.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/authorization-result.json",
+ "title": "Authorization Result",
+ "description": "Result of validating sales agent authorization for a property. Checks if the sales agent is listed in the property's adagents.json.",
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "enum": ["authorized", "unauthorized", "unknown"],
+ "description": "Authorization status: authorized (agent in adagents.json), unauthorized (agent not in adagents.json), unknown (could not fetch or parse adagents.json)"
+ },
+ "publisher_domain": {
+ "type": "string",
+ "description": "The publisher domain where adagents.json was checked"
+ },
+ "sales_agent_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "The sales agent URL that was validated"
+ },
+ "violation": {
+ "type": "object",
+ "description": "Details about the authorization failure (only present for unauthorized status)",
+ "properties": {
+ "code": {
+ "type": "string",
+ "description": "Machine-readable violation code"
+ },
+ "message": {
+ "type": "string",
+ "description": "Human-readable violation description"
+ }
+ },
+ "required": ["code", "message"],
+ "additionalProperties": false
+ }
+ },
+ "required": ["status"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/base-property-source.json b/static/schemas/source/property/base-property-source.json
new file mode 100644
index 000000000..d23b0c2bc
--- /dev/null
+++ b/static/schemas/source/property/base-property-source.json
@@ -0,0 +1,87 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/base-property-source.json",
+ "title": "Base Property Source",
+ "description": "A source of properties for a property list. Supports three selection patterns: publisher with tags, publisher with property IDs, or direct identifiers.",
+ "discriminator": {
+ "propertyName": "selection_type"
+ },
+ "oneOf": [
+ {
+ "type": "object",
+ "title": "Publisher Tags Source",
+ "description": "Select properties from a publisher by tag membership",
+ "properties": {
+ "selection_type": {
+ "type": "string",
+ "const": "publisher_tags",
+ "description": "Discriminator indicating selection by property tags within a publisher"
+ },
+ "publisher_domain": {
+ "type": "string",
+ "description": "Domain where publisher's adagents.json is hosted (e.g., 'raptive.com')",
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$"
+ },
+ "tags": {
+ "type": "array",
+ "description": "Property tags from the publisher's adagents.json. Selects all properties with these tags.",
+ "items": {
+ "$ref": "/schemas/core/property-tag.json"
+ },
+ "minItems": 1
+ }
+ },
+ "required": ["selection_type", "publisher_domain", "tags"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "title": "Publisher Property IDs Source",
+ "description": "Select specific properties from a publisher by ID",
+ "properties": {
+ "selection_type": {
+ "type": "string",
+ "const": "publisher_ids",
+ "description": "Discriminator indicating selection by specific property IDs within a publisher"
+ },
+ "publisher_domain": {
+ "type": "string",
+ "description": "Domain where publisher's adagents.json is hosted (e.g., 'raptive.com')",
+ "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$"
+ },
+ "property_ids": {
+ "type": "array",
+ "description": "Specific property IDs from the publisher's adagents.json",
+ "items": {
+ "$ref": "/schemas/core/property-id.json"
+ },
+ "minItems": 1
+ }
+ },
+ "required": ["selection_type", "publisher_domain", "property_ids"],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "title": "Direct Identifiers Source",
+ "description": "Select properties by direct identifiers (domains, app IDs, etc.) without publisher context",
+ "properties": {
+ "selection_type": {
+ "type": "string",
+ "const": "identifiers",
+ "description": "Discriminator indicating selection by direct identifiers"
+ },
+ "identifiers": {
+ "type": "array",
+ "description": "Direct property identifiers (domains, app IDs, etc.)",
+ "items": {
+ "$ref": "/schemas/core/identifier.json"
+ },
+ "minItems": 1
+ }
+ },
+ "required": ["selection_type", "identifiers"],
+ "additionalProperties": false
+ }
+ ]
+}
diff --git a/static/schemas/source/property/create-property-list-request.json b/static/schemas/source/property/create-property-list-request.json
new file mode 100644
index 000000000..b1c3185f1
--- /dev/null
+++ b/static/schemas/source/property/create-property-list-request.json
@@ -0,0 +1,40 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/create-property-list-request.json",
+ "title": "Create Property List Request",
+ "description": "Request parameters for creating a new property list",
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Human-readable name for the list"
+ },
+ "description": {
+ "type": "string",
+ "description": "Description of the list's purpose"
+ },
+ "base_properties": {
+ "type": "array",
+ "description": "Array of property sources to evaluate. Each entry is a discriminated union: publisher_tags (publisher_domain + tags), publisher_ids (publisher_domain + property_ids), or identifiers (direct identifiers). If omitted, queries the agent's entire property database.",
+ "items": {
+ "$ref": "/schemas/property/base-property-source.json"
+ }
+ },
+ "filters": {
+ "$ref": "/schemas/property/property-list-filters.json",
+ "description": "Dynamic filters to apply when resolving the list"
+ },
+ "brand_manifest": {
+ "$ref": "/schemas/core/brand-manifest.json",
+ "description": "Brand identity and requirements. When provided, the agent automatically applies appropriate rules based on brand characteristics (industry, target_audience, etc.)."
+ },
+ "context": {
+ "$ref": "/schemas/core/context.json"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["name"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/create-property-list-response.json b/static/schemas/source/property/create-property-list-response.json
new file mode 100644
index 000000000..2b608088d
--- /dev/null
+++ b/static/schemas/source/property/create-property-list-response.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/create-property-list-response.json",
+ "title": "Create Property List Response",
+ "description": "Response payload for create_property_list task",
+ "type": "object",
+ "properties": {
+ "list": {
+ "$ref": "/schemas/property/property-list.json",
+ "description": "The created property list"
+ },
+ "auth_token": {
+ "type": "string",
+ "description": "Token that can be shared with sellers to authorize fetching this list. Store this - it is only returned at creation time."
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list", "auth_token"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/delete-property-list-request.json b/static/schemas/source/property/delete-property-list-request.json
new file mode 100644
index 000000000..0e5217d26
--- /dev/null
+++ b/static/schemas/source/property/delete-property-list-request.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/delete-property-list-request.json",
+ "title": "Delete Property List Request",
+ "description": "Request parameters for deleting a property list",
+ "type": "object",
+ "properties": {
+ "list_id": {
+ "type": "string",
+ "description": "ID of the property list to delete"
+ },
+ "context": {
+ "$ref": "/schemas/core/context.json"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list_id"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/delete-property-list-response.json b/static/schemas/source/property/delete-property-list-response.json
new file mode 100644
index 000000000..655bbed52
--- /dev/null
+++ b/static/schemas/source/property/delete-property-list-response.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/delete-property-list-response.json",
+ "title": "Delete Property List Response",
+ "description": "Response payload for delete_property_list task",
+ "type": "object",
+ "properties": {
+ "deleted": {
+ "type": "boolean",
+ "description": "Whether the list was successfully deleted"
+ },
+ "list_id": {
+ "type": "string",
+ "description": "ID of the deleted list"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["deleted", "list_id"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/delivery-record.json b/static/schemas/source/property/delivery-record.json
new file mode 100644
index 000000000..29e6bc17a
--- /dev/null
+++ b/static/schemas/source/property/delivery-record.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/delivery-record.json",
+ "title": "Delivery Record",
+ "description": "A single delivery record representing impressions served to a property identifier. Used as input to validate_property_delivery.",
+ "type": "object",
+ "properties": {
+ "identifier": {
+ "$ref": "/schemas/core/identifier.json",
+ "description": "The property identifier where impressions were delivered"
+ },
+ "impressions": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Number of impressions delivered to this identifier"
+ },
+ "record_id": {
+ "type": "string",
+ "description": "Optional client-provided ID for correlating results back to source data"
+ },
+ "sales_agent_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL of the sales agent that sold this inventory. If provided, authorization is validated against the property's adagents.json."
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["identifier", "impressions"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/feature-requirement.json b/static/schemas/source/property/feature-requirement.json
new file mode 100644
index 000000000..0a75a1a21
--- /dev/null
+++ b/static/schemas/source/property/feature-requirement.json
@@ -0,0 +1,34 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/feature-requirement.json",
+ "title": "Feature Requirement",
+ "description": "A feature-based requirement for property filtering. Use min_value/max_value for quantitative features, allowed_values for binary/categorical features.",
+ "type": "object",
+ "properties": {
+ "feature_id": {
+ "type": "string",
+ "description": "Feature to evaluate (discovered via list_property_features)"
+ },
+ "min_value": {
+ "type": "number",
+ "description": "Minimum numeric value required (for quantitative features)"
+ },
+ "max_value": {
+ "type": "number",
+ "description": "Maximum numeric value allowed (for quantitative features)"
+ },
+ "allowed_values": {
+ "type": "array",
+ "description": "Values that pass the requirement (for binary/categorical features)",
+ "items": {}
+ },
+ "if_not_covered": {
+ "type": "string",
+ "enum": ["exclude", "include"],
+ "default": "exclude",
+ "description": "How to handle properties where this feature is not covered. 'exclude' (default): property is removed from the list. 'include': property passes this requirement (fail-open)."
+ }
+ },
+ "required": ["feature_id"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/get-property-features-request.json b/static/schemas/source/property/get-property-features-request.json
new file mode 100644
index 000000000..a664eb65a
--- /dev/null
+++ b/static/schemas/source/property/get-property-features-request.json
@@ -0,0 +1,68 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/get-property-features-request.json",
+ "title": "Get Property Features Request",
+ "description": "Request payload for get_property_features task. Retrieves feature values for properties from a governance agent. Supports two modes: explicit property list OR publisher-based discovery.",
+ "type": "object",
+ "properties": {
+ "properties": {
+ "type": "array",
+ "description": "Explicit list of properties to get features for. Can be domain strings or structured property references. Use this when you know exactly which properties you want.",
+ "items": {
+ "oneOf": [
+ {
+ "type": "string",
+ "description": "Domain string (e.g., 'example.com')"
+ },
+ {
+ "$ref": "/schemas/core/property-id.json"
+ }
+ ]
+ },
+ "minItems": 1,
+ "maxItems": 100
+ },
+ "publisher_domain": {
+ "type": "string",
+ "description": "Publisher domain to discover properties for. Use this to get features for all properties belonging to a publisher. Can be combined with property_types and property_tags for filtering."
+ },
+ "property_types": {
+ "type": "array",
+ "description": "Filter to specific property types (e.g., 'website', 'mobile_app', 'ctv_app'). Only applies when using publisher_domain.",
+ "items": {
+ "$ref": "/schemas/enums/property-type.json"
+ }
+ },
+ "property_tags": {
+ "type": "array",
+ "description": "Filter to properties with these tags. Only applies when using publisher_domain.",
+ "items": {
+ "$ref": "/schemas/core/property-tag.json"
+ }
+ },
+ "feature_ids": {
+ "type": "array",
+ "description": "Optional filter to specific features. If omitted, returns all available features.",
+ "items": {
+ "type": "string"
+ }
+ },
+ "context": {
+ "$ref": "/schemas/core/context.json"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "oneOf": [
+ {
+ "required": ["properties"],
+ "description": "Explicit property list mode"
+ },
+ {
+ "required": ["publisher_domain"],
+ "description": "Publisher discovery mode"
+ }
+ ],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/get-property-features-response.json b/static/schemas/source/property/get-property-features-response.json
new file mode 100644
index 000000000..d07a0d480
--- /dev/null
+++ b/static/schemas/source/property/get-property-features-response.json
@@ -0,0 +1,28 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/get-property-features-response.json",
+ "title": "Get Property Features Response",
+ "description": "Response payload for get_property_features task. Returns feature values for requested properties.",
+ "type": "object",
+ "properties": {
+ "results": {
+ "type": "array",
+ "description": "Feature values for each requested property",
+ "items": {
+ "$ref": "/schemas/property/property-feature-result.json"
+ }
+ },
+ "errors": {
+ "type": "array",
+ "description": "Errors for properties that could not be processed",
+ "items": {
+ "$ref": "/schemas/core/error.json"
+ }
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["results"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/get-property-list-request.json b/static/schemas/source/property/get-property-list-request.json
new file mode 100644
index 000000000..1fd852a76
--- /dev/null
+++ b/static/schemas/source/property/get-property-list-request.json
@@ -0,0 +1,35 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/get-property-list-request.json",
+ "title": "Get Property List Request",
+ "description": "Request parameters for retrieving a property list with resolved identifiers",
+ "type": "object",
+ "properties": {
+ "list_id": {
+ "type": "string",
+ "description": "ID of the property list to retrieve"
+ },
+ "resolve": {
+ "type": "boolean",
+ "description": "Whether to apply filters and return resolved identifiers (default: true)",
+ "default": true
+ },
+ "max_results": {
+ "type": "integer",
+ "description": "Maximum identifiers to return (for large lists)",
+ "minimum": 1
+ },
+ "cursor": {
+ "type": "string",
+ "description": "Pagination cursor for large result sets"
+ },
+ "context": {
+ "$ref": "/schemas/core/context.json"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list_id"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/get-property-list-response.json b/static/schemas/source/property/get-property-list-response.json
new file mode 100644
index 000000000..89ba3bfc5
--- /dev/null
+++ b/static/schemas/source/property/get-property-list-response.json
@@ -0,0 +1,68 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/get-property-list-response.json",
+ "title": "Get Property List Response",
+ "description": "Response payload for get_property_list task. Returns identifiers only (not full property objects or scores). Consumers should cache the resolved identifiers and refresh based on cache_valid_until.",
+ "type": "object",
+ "properties": {
+ "list": {
+ "$ref": "/schemas/property/property-list.json",
+ "description": "The property list metadata (always returned)"
+ },
+ "identifiers": {
+ "type": "array",
+ "description": "Resolved identifiers that passed filters (if resolve=true). Cache these locally for real-time use.",
+ "items": {
+ "$ref": "/schemas/core/identifier.json"
+ }
+ },
+ "total_count": {
+ "type": "integer",
+ "description": "Total number of identifiers in resolved list"
+ },
+ "returned_count": {
+ "type": "integer",
+ "description": "Number of identifiers returned in this response"
+ },
+ "pagination": {
+ "type": "object",
+ "description": "Pagination information",
+ "properties": {
+ "has_more": {
+ "type": "boolean",
+ "description": "Whether more results are available"
+ },
+ "cursor": {
+ "type": "string",
+ "description": "Cursor for next page"
+ }
+ },
+ "additionalProperties": false
+ },
+ "resolved_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the list was resolved"
+ },
+ "cache_valid_until": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Cache expiration timestamp. Re-fetch the list after this time to get updated identifiers."
+ },
+ "coverage_gaps": {
+ "type": "object",
+ "description": "Properties included in the list despite missing feature data. Only present when a feature_requirement has if_not_covered='include'. Maps feature_id to list of identifiers not covered for that feature.",
+ "additionalProperties": {
+ "type": "array",
+ "items": {
+ "$ref": "/schemas/core/identifier.json"
+ }
+ }
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/list-property-features-request.json b/static/schemas/source/property/list-property-features-request.json
new file mode 100644
index 000000000..3c16d096c
--- /dev/null
+++ b/static/schemas/source/property/list-property-features-request.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/list-property-features-request.json",
+ "title": "List Property Features Request",
+ "description": "Request payload for list_property_features task. Discovers what features a governance agent can evaluate.",
+ "type": "object",
+ "properties": {
+ "property_types": {
+ "type": "array",
+ "description": "Filter to features available for these property types",
+ "items": {
+ "type": "string"
+ }
+ },
+ "countries": {
+ "type": "array",
+ "description": "Filter to features available in these countries",
+ "items": {
+ "type": "string"
+ }
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/list-property-features-response.json b/static/schemas/source/property/list-property-features-response.json
new file mode 100644
index 000000000..efbf849fd
--- /dev/null
+++ b/static/schemas/source/property/list-property-features-response.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/list-property-features-response.json",
+ "title": "List Property Features Response",
+ "description": "Response payload for list_property_features task. Returns the features this governance agent can evaluate.",
+ "type": "object",
+ "properties": {
+ "features": {
+ "type": "array",
+ "description": "Features this agent can evaluate",
+ "items": {
+ "$ref": "/schemas/property/property-feature-definition.json"
+ }
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["features"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/list-property-lists-request.json b/static/schemas/source/property/list-property-lists-request.json
new file mode 100644
index 000000000..034c38e47
--- /dev/null
+++ b/static/schemas/source/property/list-property-lists-request.json
@@ -0,0 +1,34 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/list-property-lists-request.json",
+ "title": "List Property Lists Request",
+ "description": "Request parameters for listing property lists",
+ "type": "object",
+ "properties": {
+ "principal": {
+ "type": "string",
+ "description": "Filter to lists owned by this principal"
+ },
+ "name_contains": {
+ "type": "string",
+ "description": "Filter to lists whose name contains this string"
+ },
+ "max_results": {
+ "type": "integer",
+ "description": "Maximum lists to return",
+ "minimum": 1,
+ "default": 100
+ },
+ "cursor": {
+ "type": "string",
+ "description": "Pagination cursor"
+ },
+ "context": {
+ "$ref": "/schemas/core/context.json"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/list-property-lists-response.json b/static/schemas/source/property/list-property-lists-response.json
new file mode 100644
index 000000000..f80b67ed1
--- /dev/null
+++ b/static/schemas/source/property/list-property-lists-response.json
@@ -0,0 +1,44 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/list-property-lists-response.json",
+ "title": "List Property Lists Response",
+ "description": "Response payload for list_property_lists task",
+ "type": "object",
+ "properties": {
+ "lists": {
+ "type": "array",
+ "description": "Array of property lists (metadata only, not resolved properties)",
+ "items": {
+ "$ref": "/schemas/property/property-list.json"
+ }
+ },
+ "total_count": {
+ "type": "integer",
+ "description": "Total number of lists matching criteria"
+ },
+ "returned_count": {
+ "type": "integer",
+ "description": "Number of lists returned in this response"
+ },
+ "pagination": {
+ "type": "object",
+ "description": "Pagination information",
+ "properties": {
+ "has_more": {
+ "type": "boolean",
+ "description": "Whether more results are available"
+ },
+ "cursor": {
+ "type": "string",
+ "description": "Cursor for next page"
+ }
+ },
+ "additionalProperties": false
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["lists"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-error.json b/static/schemas/source/property/property-error.json
new file mode 100644
index 000000000..7b46b096e
--- /dev/null
+++ b/static/schemas/source/property/property-error.json
@@ -0,0 +1,31 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-error.json",
+ "title": "Property Error",
+ "description": "Error information for a property that could not be evaluated",
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string",
+ "description": "Error code",
+ "enum": [
+ "PROPERTY_NOT_FOUND",
+ "PROPERTY_NOT_MONITORED",
+ "LIST_NOT_FOUND",
+ "LIST_ACCESS_DENIED",
+ "METHODOLOGY_NOT_SUPPORTED",
+ "JURISDICTION_NOT_SUPPORTED"
+ ]
+ },
+ "property": {
+ "$ref": "/schemas/core/property.json",
+ "description": "The property that caused the error"
+ },
+ "message": {
+ "type": "string",
+ "description": "Human-readable error message"
+ }
+ },
+ "required": ["code", "message"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-feature-definition.json b/static/schemas/source/property/property-feature-definition.json
new file mode 100644
index 000000000..baaf017bb
--- /dev/null
+++ b/static/schemas/source/property/property-feature-definition.json
@@ -0,0 +1,84 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-feature-definition.json",
+ "title": "Property Feature Definition",
+ "description": "Defines a feature that a governance agent can evaluate for properties. Used in list_property_features to advertise agent capabilities.",
+ "type": "object",
+ "properties": {
+ "feature_id": {
+ "type": "string",
+ "description": "Unique identifier for this feature (e.g., 'consent_quality', 'carbon_score', 'coppa_certified')"
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name for the feature"
+ },
+ "description": {
+ "type": "string",
+ "description": "Description of what this feature measures or represents"
+ },
+ "type": {
+ "type": "string",
+ "enum": ["binary", "quantitative", "categorical"],
+ "description": "The type of values this feature produces: binary (true/false), quantitative (numeric range), categorical (enumerated values)"
+ },
+ "range": {
+ "type": "object",
+ "description": "For quantitative features, the valid range of values",
+ "properties": {
+ "min": {
+ "type": "number",
+ "description": "Minimum value"
+ },
+ "max": {
+ "type": "number",
+ "description": "Maximum value"
+ }
+ },
+ "required": ["min", "max"],
+ "additionalProperties": false
+ },
+ "allowed_values": {
+ "type": "array",
+ "description": "For categorical features, the set of valid values",
+ "items": {
+ "type": "string"
+ }
+ },
+ "coverage": {
+ "type": "object",
+ "description": "What this feature covers (empty arrays = all)",
+ "properties": {
+ "property_types": {
+ "type": "array",
+ "description": "Property types this feature applies to",
+ "items": {
+ "type": "string"
+ }
+ },
+ "countries": {
+ "type": "array",
+ "description": "Countries where this feature is available",
+ "items": {
+ "type": "string"
+ }
+ }
+ },
+ "additionalProperties": false
+ },
+ "methodology_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL to documentation explaining how this feature is calculated/measured"
+ },
+ "methodology_version": {
+ "type": "string",
+ "description": "Version identifier for the methodology (for audit trails)"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["feature_id", "name", "type", "methodology_url"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-feature-result.json b/static/schemas/source/property/property-feature-result.json
new file mode 100644
index 000000000..25a43a643
--- /dev/null
+++ b/static/schemas/source/property/property-feature-result.json
@@ -0,0 +1,43 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-feature-result.json",
+ "title": "Property Feature Result",
+ "description": "Feature values for a single property from a governance agent.",
+ "type": "object",
+ "properties": {
+ "property": {
+ "oneOf": [
+ {
+ "type": "string",
+ "description": "Domain string (e.g., 'example.com')"
+ },
+ {
+ "$ref": "/schemas/core/property-id.json"
+ }
+ ],
+ "description": "The property these features apply to"
+ },
+ "features": {
+ "type": "object",
+ "description": "Map of feature_id to feature value",
+ "additionalProperties": {
+ "$ref": "/schemas/property/property-feature-value.json"
+ }
+ },
+ "coverage_status": {
+ "type": "string",
+ "enum": ["covered", "not_covered", "pending"],
+ "description": "Whether this property is covered by this governance agent: covered (has data), not_covered (not measured), pending (measurement in progress)"
+ },
+ "last_evaluated": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When features were last evaluated for this property"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["property", "coverage_status"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-feature-value.json b/static/schemas/source/property/property-feature-value.json
new file mode 100644
index 000000000..c2b70ab71
--- /dev/null
+++ b/static/schemas/source/property/property-feature-value.json
@@ -0,0 +1,51 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-feature-value.json",
+ "title": "Property Feature Value",
+ "description": "A single feature value for a property. Structure varies by feature type (binary, quantitative, categorical).",
+ "type": "object",
+ "properties": {
+ "value": {
+ "description": "The feature value. Type depends on feature definition: boolean for binary, number for quantitative, string for categorical.",
+ "oneOf": [
+ { "type": "boolean" },
+ { "type": "number" },
+ { "type": "string" }
+ ]
+ },
+ "unit": {
+ "type": "string",
+ "description": "Unit of measurement for quantitative values (e.g., 'gCO2e/1000_impressions', 'percentage')"
+ },
+ "confidence": {
+ "type": "number",
+ "minimum": 0,
+ "maximum": 1,
+ "description": "Confidence score for this value (0-1)"
+ },
+ "measured_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When this specific value was measured"
+ },
+ "expires_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When this certification/value expires (for time-limited certifications)"
+ },
+ "methodology_version": {
+ "type": "string",
+ "description": "Version of the methodology used to calculate this value"
+ },
+ "details": {
+ "type": "object",
+ "description": "Additional vendor-specific details about this measurement",
+ "additionalProperties": true
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["value"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-feature.json b/static/schemas/source/property/property-feature.json
new file mode 100644
index 000000000..6c9939b59
--- /dev/null
+++ b/static/schemas/source/property/property-feature.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-feature.json",
+ "title": "Property Feature",
+ "description": "A discrete feature assessment for a property (e.g., from app store privacy labels)",
+ "type": "object",
+ "properties": {
+ "feature_id": {
+ "type": "string",
+ "description": "Identifier for the feature being assessed"
+ },
+ "value": {
+ "type": "string",
+ "description": "The feature value"
+ },
+ "source": {
+ "type": "string",
+ "description": "Source of the feature data (e.g., app_store_privacy_label, tcf_string)"
+ }
+ },
+ "required": ["feature_id", "value"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-list-changed-webhook.json b/static/schemas/source/property/property-list-changed-webhook.json
new file mode 100644
index 000000000..69b6c8f17
--- /dev/null
+++ b/static/schemas/source/property/property-list-changed-webhook.json
@@ -0,0 +1,60 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-list-changed-webhook.json",
+ "title": "Property List Changed Webhook",
+ "description": "Webhook notification sent when a property list's resolved properties change. Contains a summary only - recipients must call get_property_list to retrieve the updated properties. This keeps payloads small and avoids redundant data transfer.",
+ "type": "object",
+ "properties": {
+ "event": {
+ "type": "string",
+ "const": "property_list_changed",
+ "description": "The event type"
+ },
+ "list_id": {
+ "type": "string",
+ "description": "ID of the property list that changed"
+ },
+ "list_name": {
+ "type": "string",
+ "description": "Name of the property list"
+ },
+ "change_summary": {
+ "type": "object",
+ "description": "Summary of changes to the resolved list",
+ "properties": {
+ "properties_added": {
+ "type": "integer",
+ "description": "Number of properties added since last resolution"
+ },
+ "properties_removed": {
+ "type": "integer",
+ "description": "Number of properties removed since last resolution"
+ },
+ "total_properties": {
+ "type": "integer",
+ "description": "Total properties in the resolved list"
+ }
+ },
+ "additionalProperties": false
+ },
+ "resolved_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the list was re-resolved"
+ },
+ "cache_valid_until": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the consumer should refresh from the governance agent"
+ },
+ "signature": {
+ "type": "string",
+ "description": "Cryptographic signature of the webhook payload, signed with the agent's private key. Recipients MUST verify this signature."
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["event", "list_id", "resolved_at", "signature"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-list-filters.json b/static/schemas/source/property/property-list-filters.json
new file mode 100644
index 000000000..ec132f2bf
--- /dev/null
+++ b/static/schemas/source/property/property-list-filters.json
@@ -0,0 +1,47 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-list-filters.json",
+ "title": "Property List Filters",
+ "description": "Filters that dynamically modify a property list when resolved",
+ "type": "object",
+ "properties": {
+ "countries_all": {
+ "type": "array",
+ "description": "Property must have feature data for ALL listed countries (ISO codes). Required.",
+ "items": {
+ "type": "string",
+ "pattern": "^[A-Z]{2}$"
+ }
+ },
+ "channels_any": {
+ "type": "array",
+ "description": "Property must support ANY of the listed channels. Required.",
+ "items": {
+ "$ref": "/schemas/enums/channels.json"
+ }
+ },
+ "property_types": {
+ "type": "array",
+ "description": "Filter to these property types",
+ "items": {
+ "$ref": "/schemas/enums/property-type.json"
+ }
+ },
+ "feature_requirements": {
+ "type": "array",
+ "description": "Feature-based requirements. Property must pass ALL requirements (AND logic).",
+ "items": {
+ "$ref": "/schemas/property/feature-requirement.json"
+ }
+ },
+ "exclude_identifiers": {
+ "type": "array",
+ "description": "Identifiers to always exclude from results",
+ "items": {
+ "$ref": "/schemas/core/identifier.json"
+ }
+ }
+ },
+ "required": ["countries_all", "channels_any"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/property-list.json b/static/schemas/source/property/property-list.json
new file mode 100644
index 000000000..53a89a5cf
--- /dev/null
+++ b/static/schemas/source/property/property-list.json
@@ -0,0 +1,67 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/property-list.json",
+ "title": "Property List",
+ "description": "A managed property list with optional filters for dynamic evaluation. Lists are resolved at setup time and cached by orchestrators/sellers for real-time use.",
+ "type": "object",
+ "properties": {
+ "list_id": {
+ "type": "string",
+ "description": "Unique identifier for this property list"
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name for the list"
+ },
+ "description": {
+ "type": "string",
+ "description": "Description of the list's purpose"
+ },
+ "principal": {
+ "type": "string",
+ "description": "Principal identity that owns this list"
+ },
+ "base_properties": {
+ "type": "array",
+ "description": "Array of property sources to evaluate. Each entry is a discriminated union: publisher_tags (publisher_domain + tags), publisher_ids (publisher_domain + property_ids), or identifiers (direct identifiers). If omitted, queries the agent's entire property database.",
+ "items": {
+ "$ref": "/schemas/property/base-property-source.json"
+ }
+ },
+ "filters": {
+ "$ref": "/schemas/property/property-list-filters.json",
+ "description": "Dynamic filters applied when resolving the list"
+ },
+ "brand_manifest": {
+ "$ref": "/schemas/core/brand-manifest.json",
+ "description": "Brand identity used to automatically apply appropriate rules"
+ },
+ "webhook_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL to receive notifications when the resolved list changes"
+ },
+ "cache_duration_hours": {
+ "type": "integer",
+ "description": "Recommended cache duration for resolved list. Consumers should re-fetch after this period.",
+ "minimum": 1,
+ "default": 24
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the list was created"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "When the list was last modified"
+ },
+ "property_count": {
+ "type": "integer",
+ "description": "Number of properties in the resolved list (at time of last resolution)"
+ }
+ },
+ "required": ["list_id", "name"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/update-property-list-request.json b/static/schemas/source/property/update-property-list-request.json
new file mode 100644
index 000000000..d83cdb49b
--- /dev/null
+++ b/static/schemas/source/property/update-property-list-request.json
@@ -0,0 +1,49 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/update-property-list-request.json",
+ "title": "Update Property List Request",
+ "description": "Request parameters for updating an existing property list",
+ "type": "object",
+ "properties": {
+ "list_id": {
+ "type": "string",
+ "description": "ID of the property list to update"
+ },
+ "name": {
+ "type": "string",
+ "description": "New name for the list"
+ },
+ "description": {
+ "type": "string",
+ "description": "New description"
+ },
+ "base_properties": {
+ "type": "array",
+ "description": "Complete replacement for the base properties list (not a patch). Each entry is a discriminated union: publisher_tags (publisher_domain + tags), publisher_ids (publisher_domain + property_ids), or identifiers (direct identifiers).",
+ "items": {
+ "$ref": "/schemas/property/base-property-source.json"
+ }
+ },
+ "filters": {
+ "$ref": "/schemas/property/property-list-filters.json",
+ "description": "Complete replacement for the filters (not a patch)"
+ },
+ "brand_manifest": {
+ "$ref": "/schemas/core/brand-manifest.json",
+ "description": "Update brand identity and requirements"
+ },
+ "webhook_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "Update the webhook URL for list change notifications (set to empty string to remove)"
+ },
+ "context": {
+ "$ref": "/schemas/core/context.json"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list_id"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/update-property-list-response.json b/static/schemas/source/property/update-property-list-response.json
new file mode 100644
index 000000000..8258df259
--- /dev/null
+++ b/static/schemas/source/property/update-property-list-response.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/update-property-list-response.json",
+ "title": "Update Property List Response",
+ "description": "Response payload for update_property_list task",
+ "type": "object",
+ "properties": {
+ "list": {
+ "$ref": "/schemas/property/property-list.json",
+ "description": "The updated property list"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/validate-property-delivery-request.json b/static/schemas/source/property/validate-property-delivery-request.json
new file mode 100644
index 000000000..16597cf43
--- /dev/null
+++ b/static/schemas/source/property/validate-property-delivery-request.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/validate-property-delivery-request.json",
+ "title": "Validate Property Delivery Request",
+ "description": "Request payload for validate_property_delivery task. Validates delivery records against a property list to determine compliance.",
+ "type": "object",
+ "properties": {
+ "list_id": {
+ "type": "string",
+ "description": "ID of the property list to validate against"
+ },
+ "records": {
+ "type": "array",
+ "description": "Delivery records to validate. Each record represents impressions delivered to a property identifier.",
+ "items": {
+ "$ref": "/schemas/property/delivery-record.json"
+ },
+ "minItems": 1,
+ "maxItems": 10000
+ },
+ "include_compliant": {
+ "type": "boolean",
+ "default": false,
+ "description": "Include compliant records in results (default: only return non_compliant, unmodeled, and unidentified)"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list_id", "records"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/validate-property-delivery-response.json b/static/schemas/source/property/validate-property-delivery-response.json
new file mode 100644
index 000000000..be229f406
--- /dev/null
+++ b/static/schemas/source/property/validate-property-delivery-response.json
@@ -0,0 +1,167 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/validate-property-delivery-response.json",
+ "title": "Validate Property Delivery Response",
+ "description": "Response payload for validate_property_delivery task. Returns aggregate compliance statistics and per-record validation results.",
+ "type": "object",
+ "properties": {
+ "list_id": {
+ "type": "string",
+ "description": "ID of the property list validated against"
+ },
+ "summary": {
+ "type": "object",
+ "description": "Aggregate validation statistics",
+ "properties": {
+ "total_records": {
+ "type": "integer",
+ "description": "Total number of records validated"
+ },
+ "total_impressions": {
+ "type": "integer",
+ "description": "Total impressions across all records"
+ },
+ "compliant_records": {
+ "type": "integer",
+ "description": "Number of records with compliant status"
+ },
+ "compliant_impressions": {
+ "type": "integer",
+ "description": "Impressions from compliant records"
+ },
+ "non_compliant_records": {
+ "type": "integer",
+ "description": "Number of records with non_compliant status"
+ },
+ "non_compliant_impressions": {
+ "type": "integer",
+ "description": "Impressions from non_compliant records"
+ },
+ "not_covered_records": {
+ "type": "integer",
+ "description": "Number of records where identifier was recognized but no data available"
+ },
+ "not_covered_impressions": {
+ "type": "integer",
+ "description": "Impressions from not_covered records"
+ },
+ "unidentified_records": {
+ "type": "integer",
+ "description": "Number of records where identifier type was not resolvable"
+ },
+ "unidentified_impressions": {
+ "type": "integer",
+ "description": "Impressions from unidentified records"
+ }
+ },
+ "required": [
+ "total_records",
+ "total_impressions",
+ "compliant_records",
+ "compliant_impressions",
+ "non_compliant_records",
+ "non_compliant_impressions",
+ "not_covered_records",
+ "not_covered_impressions",
+ "unidentified_records",
+ "unidentified_impressions"
+ ],
+ "additionalProperties": false
+ },
+ "aggregate": {
+ "type": "object",
+ "description": "Optional aggregate measurements computed by the governance agent. Format and meaning are agent-specific.",
+ "properties": {
+ "score": {
+ "type": "number",
+ "description": "Numeric score (0-100 scale typical, but agent-defined)"
+ },
+ "grade": {
+ "type": "string",
+ "description": "Letter grade or category (e.g., 'A+', 'B-', 'Gold', 'Compliant')"
+ },
+ "label": {
+ "type": "string",
+ "description": "Human-readable summary (e.g., '85% compliant', 'High quality')"
+ },
+ "methodology_url": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL explaining how this aggregate was calculated"
+ }
+ },
+ "additionalProperties": true
+ },
+ "authorization_summary": {
+ "type": "object",
+ "description": "Aggregate authorization statistics. Only present if any records included sales_agent_url.",
+ "properties": {
+ "records_checked": {
+ "type": "integer",
+ "description": "Number of records with sales_agent_url provided"
+ },
+ "impressions_checked": {
+ "type": "integer",
+ "description": "Total impressions from records with sales_agent_url"
+ },
+ "authorized_records": {
+ "type": "integer",
+ "description": "Number of records where sales agent was authorized"
+ },
+ "authorized_impressions": {
+ "type": "integer",
+ "description": "Impressions from authorized records"
+ },
+ "unauthorized_records": {
+ "type": "integer",
+ "description": "Number of records where sales agent was NOT authorized"
+ },
+ "unauthorized_impressions": {
+ "type": "integer",
+ "description": "Impressions from unauthorized records"
+ },
+ "unknown_records": {
+ "type": "integer",
+ "description": "Number of records where authorization could not be determined (adagents.json unavailable)"
+ },
+ "unknown_impressions": {
+ "type": "integer",
+ "description": "Impressions from records where authorization could not be determined"
+ }
+ },
+ "required": [
+ "records_checked",
+ "impressions_checked",
+ "authorized_records",
+ "authorized_impressions",
+ "unauthorized_records",
+ "unauthorized_impressions",
+ "unknown_records",
+ "unknown_impressions"
+ ],
+ "additionalProperties": false
+ },
+ "results": {
+ "type": "array",
+ "description": "Per-record validation results. By default only includes non_compliant and unknown records. Set include_compliant=true to include all records.",
+ "items": {
+ "$ref": "/schemas/property/validation-result.json"
+ }
+ },
+ "validated_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Timestamp when validation was performed"
+ },
+ "list_resolved_at": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Timestamp of the property list resolution used for validation"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["list_id", "summary", "results", "validated_at"],
+ "additionalProperties": false
+}
diff --git a/static/schemas/source/property/validation-result.json b/static/schemas/source/property/validation-result.json
new file mode 100644
index 000000000..b11db55ae
--- /dev/null
+++ b/static/schemas/source/property/validation-result.json
@@ -0,0 +1,79 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "/schemas/property/validation-result.json",
+ "title": "Validation Result",
+ "description": "Result of validating a single delivery record against a property list.",
+ "type": "object",
+ "properties": {
+ "identifier": {
+ "$ref": "/schemas/core/identifier.json",
+ "description": "The identifier that was validated"
+ },
+ "record_id": {
+ "type": "string",
+ "description": "Client-provided ID from the delivery record (if provided)"
+ },
+ "status": {
+ "type": "string",
+ "enum": ["compliant", "non_compliant", "not_covered", "unidentified"],
+ "description": "Validation status: compliant (in list), non_compliant (not in list), not_covered (identifier recognized but no data available), unidentified (identifier type not resolvable by this governance agent)"
+ },
+ "impressions": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "Number of impressions from this record"
+ },
+ "violations": {
+ "type": "array",
+ "description": "Specific violations found (only present for non_compliant records)",
+ "items": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "string",
+ "description": "Machine-readable violation code"
+ },
+ "message": {
+ "type": "string",
+ "description": "Human-readable violation description"
+ },
+ "feature_id": {
+ "type": "string",
+ "description": "ID of the feature that caused the violation (only present for feature_failed violations)"
+ },
+ "requirement": {
+ "type": "object",
+ "description": "The feature requirement that was not met (only present for feature_failed violations)",
+ "properties": {
+ "min_value": {
+ "type": "number",
+ "description": "Minimum value that was required"
+ },
+ "max_value": {
+ "type": "number",
+ "description": "Maximum value that was allowed"
+ },
+ "allowed_values": {
+ "type": "array",
+ "description": "Values that would have been acceptable",
+ "items": {}
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "required": ["code", "message"],
+ "additionalProperties": false
+ }
+ },
+ "authorization": {
+ "$ref": "/schemas/property/authorization-result.json",
+ "description": "Authorization validation result (only present if sales_agent_url was provided in the delivery record)"
+ },
+ "ext": {
+ "$ref": "/schemas/core/ext.json"
+ }
+ },
+ "required": ["identifier", "status", "impressions"],
+ "additionalProperties": false
+}
diff --git a/v2.6-rc/docs/media-buy/capability-discovery/authorized-properties.mdx b/v2.6-rc/docs/media-buy/capability-discovery/authorized-properties.mdx
index 11d72b671..13264acca 100644
--- a/v2.6-rc/docs/media-buy/capability-discovery/authorized-properties.mdx
+++ b/v2.6-rc/docs/media-buy/capability-discovery/authorized-properties.mdx
@@ -268,11 +268,11 @@ For complete technical details on implementing the `adagents.json` file format,
- Validation code examples and error handling
- Security considerations and best practices
-See the **[adagents.json Tech Spec](/docs/media-buy/capability-discovery/adagents)** for complete implementation guidance.
+See the **[adagents.json Tech Spec](/docs/governance/property/adagents)** for complete implementation guidance.
## Related Documentation
- **[`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties)** - API reference for property discovery
- **[Product Discovery](/docs/media-buy/product-discovery/)** - How authorization integrates with product discovery
- **[Properties Schema](https://adcontextprotocol.org/schemas/v2/core/property.json)** - Technical property data model
-- **[adagents.json Tech Spec](/docs/media-buy/capability-discovery/adagents)** - Complete `adagents.json` implementation guide
\ No newline at end of file
+- **[adagents.json Tech Spec](/docs/governance/property/adagents)** - Complete `adagents.json` implementation guide
\ No newline at end of file
diff --git a/v2.6-rc/docs/media-buy/capability-discovery/index.mdx b/v2.6-rc/docs/media-buy/capability-discovery/index.mdx
index 885a0e16c..214806a99 100644
--- a/v2.6-rc/docs/media-buy/capability-discovery/index.mdx
+++ b/v2.6-rc/docs/media-buy/capability-discovery/index.mdx
@@ -21,7 +21,7 @@ Learn how sales agents can support standard creative formats through the referen
- Leverage the Standard Creative Agent for standard formats
- Work with publisher-specific creative agents for custom formats
-### [Authorized Properties](/docs/media-buy/capability-discovery/authorized-properties) 🔐
+### [Authorized Properties](/docs/governance/property/authorized-properties) 🔐
Learn how AdCP prevents unauthorized resale and ensures sales agents are legitimate. Understand:
- The problem of unauthorized resale in digital advertising
@@ -30,7 +30,7 @@ Learn how AdCP prevents unauthorized resale and ensures sales agents are legitim
- Property tags and large-scale authorization management
-**Cross-Protocol Foundation**: The `adagents.json` authorization system applies across ALL AdCP protocols (Media Buy, Signals, and future Curation). See the [adagents.json specification](/docs/media-buy/capability-discovery/adagents) for complete implementation details.
+**Cross-Protocol Foundation**: The `adagents.json` authorization system applies across ALL AdCP protocols (Media Buy, Signals, and future Curation). See the [adagents.json specification](/docs/governance/property/adagents) for complete implementation details.
@@ -80,4 +80,4 @@ Together, these capabilities provide the foundation for safe, efficient, and eff
- **[Creative Protocol](/docs/creative/)** - Creative agents and manifests
- **[Creative Channel Guides](/docs/creative/channels/video)** - Format examples and patterns
- **[Creative Manifests](/docs/creative/creative-manifests)** - Understanding creative specifications
-- **[adagents.json Specification](/docs/media-buy/capability-discovery/adagents)** - Publisher authorization system (applies across all AdCP protocols)
\ No newline at end of file
+- **[adagents.json Specification](/docs/governance/property/adagents)** - Publisher authorization system (applies across all AdCP protocols)
\ No newline at end of file
diff --git a/v2.6-rc/docs/media-buy/index.mdx b/v2.6-rc/docs/media-buy/index.mdx
index 0ead41042..fcaed419b 100644
--- a/v2.6-rc/docs/media-buy/index.mdx
+++ b/v2.6-rc/docs/media-buy/index.mdx
@@ -232,7 +232,7 @@ Choose your path based on your role and needs:
4. **Explore [Optimization & Reporting](/docs/media-buy/media-buys/optimization-reporting)** - Learn performance management
### **For Publishers/Sales Agents**
-1. **Learn [Authorized Properties](/docs/media-buy/capability-discovery/authorized-properties)** - Understand authorization requirements
+1. **Learn [Authorized Properties](/docs/governance/property/authorized-properties)** - Understand authorization requirements
2. **Review [Creative Formats](/docs/creative/formats)** - See supported creative specifications
3. **Study [Advanced Topics](/docs/media-buy/advanced-topics/)** - Deep dive into technical implementation
diff --git a/v2.6-rc/docs/media-buy/task-reference/list_authorized_properties.mdx b/v2.6-rc/docs/media-buy/task-reference/list_authorized_properties.mdx
index 5e7cb8ea9..55bd3fcf8 100644
--- a/v2.6-rc/docs/media-buy/task-reference/list_authorized_properties.mdx
+++ b/v2.6-rc/docs/media-buy/task-reference/list_authorized_properties.mdx
@@ -498,6 +498,6 @@ After discovering authorized properties:
## Learn More
-- [adagents.json Specification](/docs/media-buy/capability-discovery/adagents) - Publisher authorization file format
+- [adagents.json Specification](/docs/governance/property/adagents) - Publisher authorization file format
- [Property Schema](https://adcontextprotocol.org/schemas/v2/core/property.json) - Property definition structure
- [Authorization Guide](/docs/reference/authentication) - How authorization works in AdCP
diff --git a/v2.6-rc/docs/reference/implementor-faq.mdx b/v2.6-rc/docs/reference/implementor-faq.mdx
index ce93b224d..747ada9c0 100644
--- a/v2.6-rc/docs/reference/implementor-faq.mdx
+++ b/v2.6-rc/docs/reference/implementor-faq.mdx
@@ -289,7 +289,7 @@ assert(response.message.includes("policy"));
3. Verify the sales agent URL appears in `authorized_agents`
4. Reject products from unauthorized agents
-See [Authorization Validation](/docs/media-buy/capability-discovery/adagents#buyer-agent-validation) for complete requirements.
+See [Authorization Validation](/docs/governance/property/adagents#buyer-agent-validation) for complete requirements.
## Terminology