diff --git a/.changeset/assets-discovery.md b/.changeset/assets-discovery.md new file mode 100644 index 0000000000..43d94fd6e9 --- /dev/null +++ b/.changeset/assets-discovery.md @@ -0,0 +1,11 @@ +--- +"adcontextprotocol": minor +--- + +Add unified `assets` field to format schema for better asset discovery + +- Add new `assets` array to format schema with `required` boolean per asset +- Deprecate `assets_required` (still supported for backward compatibility) +- Enables full asset discovery for buyers and AI agents to see all supported assets +- Optional assets like impression trackers can now be discovered and used + diff --git a/.changeset/content-standards-fixes.md b/.changeset/content-standards-fixes.md new file mode 100644 index 0000000000..5ad3b4e16d --- /dev/null +++ b/.changeset/content-standards-fixes.md @@ -0,0 +1,9 @@ +--- +"adcontextprotocol": patch +--- + +Fix Mintlify callout syntax and add case-insensitivity notes for country/language codes + +- Convert `:::note` Docusaurus syntax to Mintlify `` components +- Add case-insensitivity documentation for country codes (ISO 3166-1 alpha-2) and language codes (ISO 639-1/BCP 47) +- Remove orphaned webhook-config.json and webhook-authentication.json schemas diff --git a/.changeset/content-standards-protocol.md b/.changeset/content-standards-protocol.md new file mode 100644 index 0000000000..2109a9108c --- /dev/null +++ b/.changeset/content-standards-protocol.md @@ -0,0 +1,19 @@ +--- +"adcontextprotocol": minor +--- + +Add Content Standards Protocol for content safety and suitability evaluation. + +Discovery tasks: +- `list_content_features`: Discover available content safety features +- `list_content_standards`: List available standards configurations +- `get_content_standards`: Retrieve content safety policies + +Management tasks: +- `create_content_standards`: Create a new standards configuration +- `update_content_standards`: Update an existing configuration +- `delete_content_standards`: Delete a configuration + +Calibration & Validation tasks: +- `calibrate_content`: Collaborative dialogue to align on policy interpretation +- `validate_content_delivery`: Batch validate delivery records diff --git a/.changeset/get-capabilities-task.md b/.changeset/get-capabilities-task.md new file mode 100644 index 0000000000..cb30a44bfa --- /dev/null +++ b/.changeset/get-capabilities-task.md @@ -0,0 +1,63 @@ +--- +"adcontextprotocol": minor +--- + +Add protocol-level get_adcp_capabilities task for cross-protocol capability discovery + +Introduces `get_adcp_capabilities` as a **protocol-level task** that works across all AdCP domain protocols. + +**Tool-based discovery:** +- AdCP discovery uses native MCP/A2A tool discovery +- Presence of `get_adcp_capabilities` tool indicates AdCP support +- Distinctive name ensures no collision with other protocols' capability tools +- Deprecates `adcp-extension.json` agent card extension + +**Cross-protocol design:** +- `adcp.major_versions` - Declare supported AdCP major versions +- `supported_protocols` - Which domain protocols are supported (media_buy, signals) +- `extensions_supported` - Extension namespaces this agent supports (e.g., `["scope3", "garm"]`) +- Protocol-specific capability sections nested under protocol name + +**Media-buy capabilities (media_buy section):** +- `features` - Optional features (inline_creative_management, property_list_filtering, content_standards) +- `execution.axe_integrations` - Agentic ad exchange URLs +- `execution.creative_specs` - VAST/MRAID version support +- `execution.targeting` - Geo targeting with granular system support +- `portfolio` - Publisher domains, channels, countries + +**Geo targeting:** +- Countries (ISO 3166-1 alpha-2) +- Regions (ISO 3166-2) +- Metros with named systems (nielsen_dma, uk_itl1, uk_itl2, eurostat_nuts2) +- Postal areas with named systems encoding country and precision (us_zip, gb_outward, ca_fsa, etc.) + +**Product filters - two models for geography:** + +*Coverage filters (for locally-bound inventory like radio, OOH, local TV):* +- `countries` - country coverage (ISO 3166-1 alpha-2) +- `regions` - region coverage (ISO 3166-2) for regional OOH, local TV +- `metros` - metro coverage ({ system, code }) for radio, DOOH, DMA-based inventory + +*Capability filters (for digital inventory with broad coverage):* +- `required_geo_targeting` - filter by seller capability with two-layer structure: + - `level`: targeting granularity (country, region, metro, postal_area) + - `system`: classification taxonomy (e.g., 'nielsen_dma', 'us_zip') +- `required_axe_integrations` - filter by AXE support +- `required_features` - filter by protocol feature support + +Use coverage filters when products ARE geographically bound (radio station = DMA). +Use capability filters when products have broad coverage and you'll target at buy time. + +**Targeting schema:** +- Updated `targeting.json` with structured geo systems +- `geo_metros` and `geo_postal_areas` now require system specification +- System names encode country and precision (us_zip, gb_outward, nielsen_dma, etc.) +- Aligns with capability declarations in get_adcp_capabilities + +**Governance capabilities (governance section):** +- `property_features` - Array of features this governance agent can evaluate +- Each feature has: `feature_id`, `type` (binary/quantitative/categorical), optional `range`/`categories` +- `methodology_url` - Optional URL to methodology documentation (helps buyers understand/compare vendor approaches) +- Deprecates `list_property_features` task (schemas removed, doc page retained with migration guide) + +**Capability contract:** If a capability is declared, the seller MUST honor it. diff --git a/.changeset/hip-numbers-train.md b/.changeset/hip-numbers-train.md new file mode 100644 index 0000000000..52ccb26a2e --- /dev/null +++ b/.changeset/hip-numbers-train.md @@ -0,0 +1,7 @@ +--- +"adcontextprotocol": minor +--- + +Add privacy_policy_url field to brand manifest and adagents.json schemas + +Enables consumer consent flows by providing a link to advertiser/publisher privacy policies. AI platforms can use this to present explicit privacy choices to users before data handoff. Works alongside MyTerms/IEEE P7012 discovery for machine-readable privacy terms. diff --git a/.changeset/loud-bushes-train.md b/.changeset/loud-bushes-train.md new file mode 100644 index 0000000000..78db9bd2ba --- /dev/null +++ b/.changeset/loud-bushes-train.md @@ -0,0 +1,15 @@ +--- +"adcontextprotocol": minor +--- + +Clarify creative handling in media buy operations: + +**Breaking:** Replace `creative_ids` with `creative_assignments` in `create_media_buy` and `update_media_buy` +- `creative_assignments` supports optional `weight` and `placement_ids` for granular control +- Simple assignment: `{ "creative_id": "my_creative" }` (weight/placement optional) +- Advanced assignment: `{ "creative_id": "my_creative", "weight": 60, "placement_ids": ["p1"] }` + +**Clarifications:** +- `creatives` array creates NEW creatives only (add `CREATIVE_ID_EXISTS` error) +- `delete_missing` in sync_creatives cannot delete creatives in active delivery (`CREATIVE_IN_ACTIVE_DELIVERY` error) +- Document that existing library creatives should be managed via `sync_creatives` diff --git a/.changeset/media-channel-taxonomy.md b/.changeset/media-channel-taxonomy.md new file mode 100644 index 0000000000..44576e35ac --- /dev/null +++ b/.changeset/media-channel-taxonomy.md @@ -0,0 +1,13 @@ +--- +"adcontextprotocol": major +--- + +Add Media Channel Taxonomy specification with standardized channel definitions. + +**BREAKING**: Replaces channel enum values (display, video, audio, native, retail β†’ display, olv, social, search, ctv, etc.) + +- Introduces 19 planning-oriented media channels representing how buyers allocate budget +- Channels: display, olv, social, search, ctv, linear_tv, radio, streaming_audio, podcast, dooh, ooh, print, cinema, email, gaming, retail_media, influencer, affiliate, product_placement +- Adds desktop_app property type for Electron/Chromium wrapper applications +- Clear distinction between channels (planning abstractions), property types (addressable surfaces), and formats (how ads render) +- Includes migration guide and edge cases documentation diff --git a/.changeset/openai-product-feed.md b/.changeset/openai-product-feed.md new file mode 100644 index 0000000000..0252df2dac --- /dev/null +++ b/.changeset/openai-product-feed.md @@ -0,0 +1,9 @@ +--- +"adcontextprotocol": minor +--- + +Add OpenAI Commerce integration to brand manifest + +- Add `openai_product_feed` as a supported feed format for product catalogs +- Add `agentic_checkout` object to enable AI agents to complete purchases via structured checkout APIs +- Document field mapping from Google Merchant Center to OpenAI Product Feed spec diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..8efb78af9a --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,8 @@ +{ + "mode": "pre", + "tag": "beta", + "initialVersions": { + "adcontextprotocol": "3.0.0-beta.1" + }, + "changesets": [] +} diff --git a/.changeset/property-governance-protocol.md b/.changeset/property-governance-protocol.md new file mode 100644 index 0000000000..882945edce --- /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/.changeset/simplify-filters.md b/.changeset/simplify-filters.md new file mode 100644 index 0000000000..ec380ec43f --- /dev/null +++ b/.changeset/simplify-filters.md @@ -0,0 +1,3 @@ +--- +--- + diff --git a/.changeset/tricky-ideas-try.md b/.changeset/tricky-ideas-try.md new file mode 100644 index 0000000000..eee124102b --- /dev/null +++ b/.changeset/tricky-ideas-try.md @@ -0,0 +1,4 @@ +--- +--- + +docs: Document property_features field for governance agent discovery in adagents.json diff --git a/.changeset/whole-sides-join.md b/.changeset/whole-sides-join.md new file mode 100644 index 0000000000..3c74f7163d --- /dev/null +++ b/.changeset/whole-sides-join.md @@ -0,0 +1,4 @@ +--- +--- + +Add 'archived' status to creative-status enum and document default exclusion behavior in list_creatives. Remove erroneous status field from assignment response (assignments are binary - they exist or don't). Replace approved boolean with status enum in creative-asset.json for consistency. Rename package_name to buyer_ref in assignment response to match package schema. diff --git a/.github/workflows/broken-links.yml b/.github/workflows/broken-links.yml index 4ab19483e9..fed80be4bf 100644 --- a/.github/workflows/broken-links.yml +++ b/.github/workflows/broken-links.yml @@ -2,9 +2,9 @@ name: Check for Broken Links on: push: - branches: [ main, develop ] + branches: [ main, develop, '2.6.x' ] pull_request: - branches: [ main, develop ] + branches: [ main, develop, '2.6.x' ] workflow_run: workflows: ["Sync Versioned Docs"] types: diff --git a/.github/workflows/changeset-check.yml b/.github/workflows/changeset-check.yml index 346ed6202c..267de5f1f6 100644 --- a/.github/workflows/changeset-check.yml +++ b/.github/workflows/changeset-check.yml @@ -2,7 +2,7 @@ name: Changeset Check on: pull_request: - branches: [main] + branches: [main, '2.6.x'] jobs: check: diff --git a/.github/workflows/check-testable-snippets.yml b/.github/workflows/check-testable-snippets.yml index e3ef8c2785..f28614b7da 100644 --- a/.github/workflows/check-testable-snippets.yml +++ b/.github/workflows/check-testable-snippets.yml @@ -2,6 +2,7 @@ name: Check Testable Snippets on: pull_request: + branches: [main, '2.6.x'] paths: - 'docs/**/*.md' - 'docs/**/*.mdx' diff --git a/.github/workflows/release-docs.yml b/.github/workflows/release-docs.yml new file mode 100644 index 0000000000..ee89354329 --- /dev/null +++ b/.github/workflows/release-docs.yml @@ -0,0 +1,155 @@ +name: Release Documentation Snapshot + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: 'Version to snapshot (e.g., 2.6.0)' + required: true + type: string + +jobs: + snapshot-docs: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Determine version + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ inputs.version }}" + else + # Extract version from tag (v3.0.0-beta.1 -> 3.0.0-beta.1) + VERSION="${GITHUB_REF_NAME#v}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + + # Extract major.minor for docs.json version label + # For "3.0.0-beta.1" -> "3.0-beta" + # For "3.0.1" -> "3.0" + if [[ "$VERSION" == *"-"* ]]; then + # Has prerelease suffix (e.g., 3.0.0-beta.1 -> 3.0-beta) + BASE_VERSION=$(echo "$VERSION" | cut -d- -f1) + PRERELEASE_TAG=$(echo "$VERSION" | cut -d- -f2 | cut -d. -f1) + MAJOR_MINOR="$(echo "$BASE_VERSION" | cut -d. -f1,2)-$PRERELEASE_TAG" + else + # Standard version (e.g., 3.0.1 -> 3.0) + MAJOR_MINOR=$(echo "$VERSION" | cut -d. -f1,2) + fi + echo "major_minor=$MAJOR_MINOR" >> $GITHUB_OUTPUT + + - name: Checkout main branch + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Fetch release tag + if: github.event_name != 'workflow_dispatch' + run: git fetch origin tag ${{ github.ref_name }} + + - name: Create docs snapshot + run: | + VERSION="${{ steps.version.outputs.version }}" + + # Determine source ref + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + SOURCE_REF="v$VERSION" + else + SOURCE_REF="${{ github.ref_name }}" + fi + + # Create dist/docs directory if it doesn't exist + mkdir -p dist/docs + + # Remove existing version if present (allows re-running) + rm -rf "dist/docs/$VERSION" + + # Extract docs from the release tag + git archive "$SOURCE_REF" -- docs | tar -x -C "dist/docs/$VERSION" --strip-components=1 || { + mkdir -p "dist/docs/$VERSION" + git archive "$SOURCE_REF" -- docs | tar -x + mv docs/* "dist/docs/$VERSION/" + rmdir docs 2>/dev/null || true + } + + echo "Created docs snapshot at dist/docs/$VERSION" + ls -la "dist/docs/$VERSION" | head -10 + + - name: Update docs.json + run: | + VERSION="${{ steps.version.outputs.version }}" + MAJOR_MINOR="${{ steps.version.outputs.major_minor }}" + + # Check if this major.minor version already exists in docs.json + EXISTING=$(jq -r ".navigation.versions[] | select(.version == \"$MAJOR_MINOR\") | .version" docs.json) + + if [ -n "$EXISTING" ]; then + echo "Version $MAJOR_MINOR already exists in docs.json, updating paths to $VERSION" + # Update existing version's paths to point to the new patch version + # Regex matches both standard (3.0.1) and prerelease (3.0.0-beta.1) versions + jq --arg old_pattern "dist/docs/[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z]+\\.[0-9]+)?/" \ + --arg new_path "dist/docs/$VERSION/" \ + --arg major_minor "$MAJOR_MINOR" \ + ' + def update_paths: + if type == "array" then + map(update_paths) + elif type == "object" then + if has("pages") then + .pages = (.pages | update_paths) + else + . + end + elif type == "string" then + if test("^dist/docs/[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z]+\\.[0-9]+)?/") then + sub("^dist/docs/[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z]+\\.[0-9]+)?/"; $new_path) + else + . + end + else + . + end; + + .navigation.versions = [ + .navigation.versions[] | + if .version == $major_minor then + .groups = (.groups | update_paths) + else + . + end + ] + ' docs.json > /tmp/updated-docs.json + mv /tmp/updated-docs.json docs.json + else + echo "Version $MAJOR_MINOR is new, adding to docs.json" + # This is a new major.minor version - would need manual nav structure + # For now, just log a warning + echo "::warning::New major.minor version $MAJOR_MINOR requires manual docs.json navigation setup" + fi + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore: snapshot docs for v${{ steps.version.outputs.version }}" + title: "chore: snapshot docs for v${{ steps.version.outputs.version }}" + body: | + This PR creates a documentation snapshot for release v${{ steps.version.outputs.version }}. + + **Changes:** + - Added `dist/docs/${{ steps.version.outputs.version }}/` with frozen documentation + - Updated `docs.json` to reference the new snapshot + + Auto-generated by the release-docs workflow. + branch: auto/docs-v${{ steps.version.outputs.version }} + delete-branch: true + add-paths: | + dist/docs/${{ steps.version.outputs.version }}/ + docs.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c5665ce6d3..176da7a247 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - '2.6.x' concurrency: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/schema-validation.yml b/.github/workflows/schema-validation.yml index 7d772503ff..a599e1adb5 100644 --- a/.github/workflows/schema-validation.yml +++ b/.github/workflows/schema-validation.yml @@ -2,9 +2,9 @@ name: JSON Schema Validation on: push: - branches: [ main, develop ] + branches: [ main, develop, '2.6.x' ] pull_request: - branches: [ main, develop ] + branches: [ main, develop, '2.6.x' ] jobs: schema-validation: diff --git a/.github/workflows/sync-versioned-docs.yml b/.github/workflows/sync-versioned-docs.yml deleted file mode 100644 index a641090206..0000000000 --- a/.github/workflows/sync-versioned-docs.yml +++ /dev/null @@ -1,110 +0,0 @@ -name: Sync Versioned Docs and Schemas - -on: - push: - branches: - - main - - 2.6.x - workflow_dispatch: # Allow manual trigger - -jobs: - sync-docs: - runs-on: ubuntu-latest - # Only run if triggered by main branch or manually, or if 2.6.x changes - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/2.6.x' || github.event_name == 'workflow_dispatch' - - steps: - - name: Checkout main branch - uses: actions/checkout@v4 - with: - ref: main - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Fetch 2.6.x branch - run: git fetch origin 2.6.x - - - name: Sync docs from 2.6.x to v2.6-rc - run: | - # Remove existing v2.6-rc/docs to ensure clean sync - rm -rf v2.6-rc/docs - mkdir -p v2.6-rc - - # Extract docs folder from 2.6.x branch - git archive origin/2.6.x -- docs | tar -x -C v2.6-rc/ - - - name: Sync schemas from 2.6.x to dist/schemas/2.6.0 - run: | - # Check if dist/schemas/2.6.0 exists in 2.6.x branch - if git ls-tree -d origin/2.6.x -- dist/schemas/2.6.0 > /dev/null 2>&1; then - # Remove existing 2.6.0 schemas to ensure clean sync - rm -rf dist/schemas/2.6.0 - - # Extract dist/schemas/2.6.0 folder from 2.6.x branch - git archive origin/2.6.x -- dist/schemas/2.6.0 | tar -x - echo "Synced dist/schemas/2.6.0 from 2.6.x branch" - else - echo "dist/schemas/2.6.0 does not exist in 2.6.x branch, skipping" - fi - - - name: Sync navigation from 2.6.x docs.json - run: | - # Extract docs.json from 2.6.x branch - git show origin/2.6.x:docs.json > /tmp/source-docs.json - - # Create jq script to transform navigation - # Takes the first version's groups from 2.6.x (which has the actual content) - # and transforms all paths by prepending "v2.6-rc/" - cat << 'JQSCRIPT' > /tmp/transform.jq - def transform_paths: - if type == "array" then - map(transform_paths) - elif type == "object" then - if has("pages") then - .pages = (.pages | transform_paths) - else - . - end - elif type == "string" then - "v2.6-rc/" + . - else - . - end; - - # $source is from --slurpfile (array), so use $source[0] - ($source[0].navigation.versions[0].groups | transform_paths) as $transformed | - (.navigation.versions | to_entries | map(select(.value.version == "2.6-rc")) | .[0].key) as $idx | - if $idx != null then - .navigation.versions[$idx].groups = $transformed - else - . - end - JQSCRIPT - - # Apply transformation: read source groups from 2.6.x, transform paths, update main's docs.json - jq --slurpfile source /tmp/source-docs.json -f /tmp/transform.jq docs.json > /tmp/updated-docs.json - - # Replace docs.json with updated version - mv /tmp/updated-docs.json docs.json - - echo "Synced navigation from 2.6.x docs.json to v2.6-rc version" - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: "chore: sync v2.6-rc docs and schemas from 2.6.x branch" - title: "chore: sync v2.6-rc docs and schemas from 2.6.x branch" - body: | - This PR syncs from the 2.6.x branch: - - Documentation to `v2.6-rc/docs/` - - Schemas to `dist/schemas/2.6.0/` (if available) - - Navigation config from 2.6.x's docs.json to main's v2.6-rc version - - Auto-generated by the sync-versioned-docs workflow. - branch: auto/sync-v2.6-rc - delete-branch: true - add-paths: | - v2.6-rc/ - dist/schemas/2.6.0/ - docs.json diff --git a/.gitignore b/.gitignore index d2569fc260..a0a98316a4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,10 @@ server/node_modules # Production /build -# Ignore dist except for released schemas +# Ignore dist except for released schemas and docs /dist/* !/dist/schemas +!/dist/docs /dist/schemas/latest /dist/schemas/v* diff --git a/.husky/pre-push b/.husky/pre-push index b39a618810..1e49d08830 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -8,11 +8,11 @@ npm run verify-version-sync echo "πŸ” Validating Mintlify documentation..." # Check for broken internal links -# Temporarily remove v2.6-rc docs which have WIP governance links -rm -rf v2.6-rc +# Temporarily remove archived docs which have version-specific links +rm -rf dist/docs npx --yes mintlify@latest broken-links BROKEN_LINKS_EXIT=$? -git checkout v2.6-rc +git checkout dist/docs 2>/dev/null || true if [ $BROKEN_LINKS_EXIT -ne 0 ]; then echo "❌ Mintlify broken links check failed. Please fix broken links before pushing." exit 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 64c5dc3b93..2a983990bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,148 @@ # Changelog +## 2.6.0 + +### Major Changes + +- Add Content Standards Protocol for brand safety and suitability evaluation (#621) + + **New Protocol:** + + Introduces a comprehensive content standards framework enabling buyers to define, calibrate, and enforce brand safety policies across advertising placements. + + **New Tasks:** + - `list_content_standards` - List available content standards configurations + - `get_content_standards` - Retrieve full standards configuration with policy details + - `create_content_standards` - Create new content standards configuration + - `update_content_standards` - Update existing content standards configuration + - `calibrate_content` - Collaborative calibration dialogue for policy alignment + - `validate_content_delivery` - Batch validate delivery records against standards + - `get_media_buy_artifacts` - Retrieve content artifacts from media buys for validation + + **New Schemas:** + - `content-standards.json` - Reusable content standards configuration + - `content-standards-artifact.json` - Content artifact for evaluation + - `artifact-webhook-payload.json` - Webhook payload for artifact delivery + +- Add Property Governance Protocol for AdCP 3.0 (#588) + + **New Protocol:** + + Enables governance agents to evaluate properties against feature-based requirements for brand safety, content quality, and compliance. + + **New Tasks:** + - `list_property_features` - Discover governance agent capabilities + - `create_property_list` - Create managed property lists with filters + - `update_property_list` - Update existing property lists + - `get_property_list` - Retrieve property list with resolved properties + - `list_property_lists` - List all property lists + - `delete_property_list` - Delete a property list + + **New Schemas:** + - `property-feature-definition.json` - Feature definition schema + - `property-feature.json` - Feature assessment schema + - `feature-requirement.json` - Feature-based requirement schema + - `property-list.json` - Managed property list schema + - `property-list-filters.json` - Dynamic filter schema + - `property-list-changed-webhook.json` - Webhook payload for list changes + +### Minor Changes + +- Add unified `assets` field to format schema for better asset discovery + + **Schema Changes:** + + - **format.json**: Add new `assets` array field that includes both required and optional assets + - **format.json**: Deprecate `assets_required` (still supported for backward compatibility) + + **Rationale:** + + Previously, buyers and AI agents could only see required assets via `assets_required`. There was no way to discover optional assets that enhance creatives (companion banners, third-party tracking pixels, etc.). + + Since each asset already has a `required` boolean field, we introduced a unified `assets` array where: + - `required: true` - Asset MUST be provided for a valid creative + - `required: false` - Asset is optional, enhances the creative when provided + + This enables: + - **Full asset discovery**: Buyers and AI agents can see ALL assets a format supports + - **Richer creatives**: Optional assets like impression trackers can now be discovered and used + - **Cleaner schema**: Single array instead of two separate arrays + + **Example:** + + ```json + { + "format_id": { "agent_url": "https://creative.adcontextprotocol.org", "id": "video_30s" }, + "assets": [ + { "item_type": "individual", "asset_id": "video_file", "asset_type": "video", "required": true }, + { "item_type": "individual", "asset_id": "end_card", "asset_type": "image", "required": false }, + { "item_type": "individual", "asset_id": "impression_tracker", "asset_type": "url", "required": false } + ] + } + ``` + + **Migration:** Non-breaking change. `assets_required` is deprecated but still supported. New implementations should use `assets`. + +- Add typed extensions infrastructure with auto-discovery (#648) + + **New Feature:** + + Introduces a typed extension system allowing vendors and domains to add custom data to AdCP schemas in a discoverable, validated way. + + **New Schemas:** + - `extensions/extension-meta.json` - Meta schema for extension definitions + - `extensions/index.json` - Auto-generated registry of all extensions + - `protocols/adcp-extension.json` - AdCP extension for agent cards + + **Benefits:** + - Vendor-specific data without polluting core schemas + - Auto-discovery of available extensions + - Validation support for extension data + +- Add OpenAI Commerce integration to brand manifest (#802) + + **Schema Changes:** + - **brand-manifest.json**: Add `openai_commerce` field for OpenAI shopping integration + + Enables brands to include their OpenAI Commerce merchant ID for AI-powered shopping experiences. + +- Add privacy_policy_url to brand manifest and adagents.json (#801) + + **Schema Changes:** + - **brand-manifest.json**: Add optional `privacy_policy_url` field + - **adagents.json**: Add optional `privacy_policy_url` field + + Enables publishers and brands to declare their privacy policy URLs for compliance and transparency. + +- Refactor: replace creative_ids with creative_assignments (#794) + + **Breaking Change:** + + Package schema now uses `creative_assignments` array instead of `creative_ids` for more flexible creative-to-package mapping with placement support. + + **Migration:** + ```json + // Before + { "creative_ids": ["creative_1", "creative_2"] } + + // After + { "creative_assignments": [ + { "creative_id": "creative_1" }, + { "creative_id": "creative_2", "placement_ids": ["homepage_banner"] } + ]} + ``` + +### Patch Changes + +- fix: Mintlify callout syntax and case-insensitivity docs (#834) +- fix: Convert governance docs to relative links (#820) +- build: rebuild dist/schemas/2.6.0 with impressions and paused fields +- chore: regenerate dist/schemas/2.6.0 with additionalProperties: true +- ci: add 2.6.x branch to all workflows +- docs: add deprecated assets_required examples with deprecation comments +- schema: make 'required' field mandatory in assets array and nested repeatable_group assets +- schema: add formal deprecated: true to assets_required field + ## 2.5.3 ### Patch Changes diff --git a/v2.6-rc/docs/adcp-flows.png b/dist/docs/2.5.3/adcp-flows.png similarity index 100% rename from v2.6-rc/docs/adcp-flows.png rename to dist/docs/2.5.3/adcp-flows.png diff --git a/v2.6-rc/docs/adcp_flows.png b/dist/docs/2.5.3/adcp_flows.png similarity index 100% rename from v2.6-rc/docs/adcp_flows.png rename to dist/docs/2.5.3/adcp_flows.png diff --git a/v2.6-rc/docs/community/working-group.mdx b/dist/docs/2.5.3/community/working-group.mdx similarity index 100% rename from v2.6-rc/docs/community/working-group.mdx rename to dist/docs/2.5.3/community/working-group.mdx diff --git a/v2.6-rc/docs/contributing/testable-examples-demo.md b/dist/docs/2.5.3/contributing/testable-examples-demo.md similarity index 100% rename from v2.6-rc/docs/contributing/testable-examples-demo.md rename to dist/docs/2.5.3/contributing/testable-examples-demo.md diff --git a/v2.6-rc/docs/contributing/testable-snippets.md b/dist/docs/2.5.3/contributing/testable-snippets.md similarity index 100% rename from v2.6-rc/docs/contributing/testable-snippets.md rename to dist/docs/2.5.3/contributing/testable-snippets.md diff --git a/v2.6-rc/docs/creative/asset-types.mdx b/dist/docs/2.5.3/creative/asset-types.mdx similarity index 91% rename from v2.6-rc/docs/creative/asset-types.mdx rename to dist/docs/2.5.3/creative/asset-types.mdx index 927fbedf1f..c3d58aa685 100644 --- a/v2.6-rc/docs/creative/asset-types.mdx +++ b/dist/docs/2.5.3/creative/asset-types.mdx @@ -338,9 +338,7 @@ The keys in the assets object correspond to the `asset_id` values defined in the ## Usage in Creative Formats -Creative formats specify their assets using the `assets` array. Each asset has a `required` boolean: -- **`required: true`** - Asset MUST be provided for a valid creative -- **`required: false`** - Asset is optional, enhances the creative (e.g., companion banners, third-party tracking pixels) +Creative formats specify their required assets using `assets_required` (an array): ```json { @@ -348,38 +346,12 @@ Creative formats specify their assets using the `assets` array. Each asset has a "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s_hosted" }, - "assets": [ - { - "item_type": "individual", - "asset_id": "video_file", - "asset_type": "video", - "required": true, - "requirements": { - "duration_seconds": 15, - "acceptable_formats": ["mp4"], - "acceptable_codecs": ["h264"], - "acceptable_resolutions": ["1920x1080", "1280x720"], - "max_file_size_mb": 30 - } - }, - { - "item_type": "individual", - "asset_id": "impression_tracker", - "asset_type": "url", - "required": false, - "requirements": { - "format": ["url"], - "description": "Third-party impression tracking pixel URL" - } - } - ], - - // DEPRECATED: Use "assets" above instead. Kept for backward compatibility. "assets_required": [ { "item_type": "individual", "asset_id": "video_file", "asset_type": "video", + "required": true, "requirements": { "duration_seconds": 15, "acceptable_formats": ["mp4"], @@ -392,10 +364,6 @@ Creative formats specify their assets using the `assets` array. Each asset has a } ``` - -**Backward Compatibility**: The `assets_required` field is deprecated but still supported. Existing implementations can continue using `assets_required` for required assets only. New implementations should use `assets` with the `required` boolean for full asset discovery. - - ## Repeatable Asset Groups For formats with asset sequences (like carousels, slideshows, stories), see the [Carousel & Multi-Asset Formats guide](/docs/creative/channels/carousels) for complete documentation on repeatable asset group patterns. diff --git a/v2.6-rc/docs/creative/brand-manifest.mdx b/dist/docs/2.5.3/creative/brand-manifest.mdx similarity index 78% rename from v2.6-rc/docs/creative/brand-manifest.mdx rename to dist/docs/2.5.3/creative/brand-manifest.mdx index cb80d8f985..692a6fe993 100644 --- a/v2.6-rc/docs/creative/brand-manifest.mdx +++ b/dist/docs/2.5.3/creative/brand-manifest.mdx @@ -22,7 +22,6 @@ Brand manifests solve a key problem: how to efficiently identify advertisers and ### Key Benefits - **Know Your Customer**: Publishers can verify advertisers meet their standards -- **Privacy Transparency**: Link to privacy policy for consumer consent flows - **Minimal Friction**: Start with just a name or URL, expand as needed - **Cacheable**: Same brand manifest reused across all requests - **Standardized**: Consistent format across all AdCP implementations @@ -244,7 +243,6 @@ The structure of the brand manifest object itself (whether provided inline or ho | Field | Type | Description | |-------|------|-------------| | `name` | string | Brand or business name | -| `privacy_policy_url` | string (uri) | URL to the brand's privacy policy for consumer consent flows | | `logos` | Logo[] | Brand logo assets with semantic tags | | `colors` | Colors | Brand color palette (hex format) | | `fonts` | Fonts | Brand typography guidelines | @@ -327,21 +325,10 @@ The structure of the brand manifest object itself (whether provided inline or ho ```typescript { feed_url: string; // URL to product catalog feed - feed_format?: string; // Format: "google_merchant_center" | "facebook_catalog" | "openai_product_feed" | "custom" + feed_format?: string; // Format of the product feed (default: "google_merchant_center") categories?: string[]; // Product categories available in the catalog last_updated?: string; // When the product catalog was last updated (ISO 8601) update_frequency?: string; // How frequently the catalog is updated - agentic_checkout?: AgenticCheckout; // Optional checkout endpoint configuration -} -``` - -### AgenticCheckout Object - -```typescript -{ - endpoint: string; // Base URL for checkout session API - spec: string; // Checkout API specification (e.g., "openai_agentic_checkout_v1") - supported_payment_providers?: string[]; // Payment providers (e.g., ["stripe", "adyen"]) } ``` @@ -472,7 +459,7 @@ Large retailers should provide product feeds: } ``` -**Supported Feed Formats**: Google Merchant Center, Facebook Catalog, [OpenAI Product Feed](https://developers.openai.com/commerce/specs/feed), Custom +**Supported Feed Formats**: RSS, JSON Feed, Product CSV ### 5. Asset Libraries for Enterprise @@ -505,85 +492,6 @@ Enterprise brands with large asset libraries should provide explicit assets: } ``` -## Privacy Integration - -Brand manifests support privacy transparency through the `privacy_policy_url` field. This enables AI platforms to present explicit privacy choices to users before sharing personal data with advertisers. - -### Consumer Consent Flow - -When an AI assistant helps a user engage with an advertiser (booking a flight, making a purchase, etc.), the platform can use the brand manifest's privacy policy URL to: - -1. **Present explicit consent**: "May I share your details with Delta? [View their privacy policy]" -2. **Enable informed decisions**: Users can review data practices before data handoff -3. **Support machine-readable terms**: Works alongside [MyTerms/IEEE P7012](https://myterms.info) for automated privacy negotiation - -### Example with Privacy Policy - -```json -{ - "$schema": "https://adcontextprotocol.org/schemas/v2/core/brand-manifest.json", - "name": "Delta Airlines", - "url": "https://delta.com", - "privacy_policy_url": "https://delta.com/privacy" -} -``` - -### MyTerms Discovery - -For advertisers implementing [IEEE P7012 (MyTerms)](https://myterms.info), AI platforms can discover machine-readable privacy terms from the advertiser's domain (e.g., `/.well-known/myterms`). The brand manifest's `privacy_policy_url` serves as the human-readable fallback and explicit consent path. - -## Agentic Commerce Integration - -Brand manifests support integration with AI commerce platforms through the `product_catalog` field. This enables AI agents to discover products and complete purchases on behalf of users. - -### OpenAI Commerce - -For merchants implementing [OpenAI's Commerce specifications](https://developers.openai.com/commerce), the brand manifest provides a bridge: - -```json -{ - "$schema": "https://adcontextprotocol.org/schemas/v2/core/brand-manifest.json", - "name": "Shop Example", - "url": "https://shopexample.com", - "privacy_policy_url": "https://shopexample.com/privacy", - "product_catalog": { - "feed_url": "https://shopexample.com/products.jsonl.gz", - "feed_format": "openai_product_feed", - "update_frequency": "daily", - "agentic_checkout": { - "endpoint": "https://api.shopexample.com/checkout_sessions", - "spec": "openai_agentic_checkout_v1", - "supported_payment_providers": ["stripe", "adyen"] - } - } -} -``` - -**Key fields for OpenAI Commerce:** - -| Field | Description | -|-------|-------------| -| `feed_format: "openai_product_feed"` | Indicates the feed conforms to [OpenAI's Product Feed spec](https://developers.openai.com/commerce/specs/feed) | -| `agentic_checkout.endpoint` | Base URL for [OpenAI's Agentic Checkout API](https://developers.openai.com/commerce/specs/checkout) | -| `agentic_checkout.spec` | Version identifier for the checkout spec | - -### Feed Format Mapping - -If you have an existing Google Merchant Center feed, here's how key fields map to OpenAI's spec: - -| OpenAI Field | Google Merchant Center | Notes | -|--------------|----------------------|-------| -| `item_id` | `id` | Direct mapping | -| `title` | `title` | Direct mapping | -| `description` | `description` | Direct mapping | -| `url` | `link` | Direct mapping | -| `brand` | `brand` | Direct mapping | -| `price` | `price` | OpenAI uses number + currency code | -| `availability` | `availability` | Same enum values | -| `image_url` | `image_link` | Direct mapping | -| `is_eligible_search` | N/A | OpenAI-specific flag | -| `is_eligible_checkout` | N/A | OpenAI-specific flag | - ## Evolution and Versioning Brand cards are versioned using the `metadata.version` field: diff --git a/v2.6-rc/docs/creative/channels/audio.mdx b/dist/docs/2.5.3/creative/channels/audio.mdx similarity index 100% rename from v2.6-rc/docs/creative/channels/audio.mdx rename to dist/docs/2.5.3/creative/channels/audio.mdx diff --git a/v2.6-rc/docs/creative/channels/carousels.mdx b/dist/docs/2.5.3/creative/channels/carousels.mdx similarity index 100% rename from v2.6-rc/docs/creative/channels/carousels.mdx rename to dist/docs/2.5.3/creative/channels/carousels.mdx diff --git a/v2.6-rc/docs/creative/channels/display.mdx b/dist/docs/2.5.3/creative/channels/display.mdx similarity index 100% rename from v2.6-rc/docs/creative/channels/display.mdx rename to dist/docs/2.5.3/creative/channels/display.mdx diff --git a/v2.6-rc/docs/creative/channels/dooh.mdx b/dist/docs/2.5.3/creative/channels/dooh.mdx similarity index 100% rename from v2.6-rc/docs/creative/channels/dooh.mdx rename to dist/docs/2.5.3/creative/channels/dooh.mdx diff --git a/v2.6-rc/docs/creative/channels/video.mdx b/dist/docs/2.5.3/creative/channels/video.mdx similarity index 100% rename from v2.6-rc/docs/creative/channels/video.mdx rename to dist/docs/2.5.3/creative/channels/video.mdx diff --git a/v2.6-rc/docs/creative/creative-manifests.mdx b/dist/docs/2.5.3/creative/creative-manifests.mdx similarity index 100% rename from v2.6-rc/docs/creative/creative-manifests.mdx rename to dist/docs/2.5.3/creative/creative-manifests.mdx diff --git a/v2.6-rc/docs/creative/formats.mdx b/dist/docs/2.5.3/creative/formats.mdx similarity index 88% rename from v2.6-rc/docs/creative/formats.mdx rename to dist/docs/2.5.3/creative/formats.mdx index ba6b100d4f..54ae1f1c23 100644 --- a/v2.6-rc/docs/creative/formats.mdx +++ b/dist/docs/2.5.3/creative/formats.mdx @@ -222,60 +222,13 @@ Formats are JSON objects with the following key fields: }, "name": "30-Second Hosted Video", "type": "video", - "assets": [ - { - "item_type": "individual", - "asset_id": "video_file", - "asset_type": "video", - "asset_role": "hero_video", - "required": true, - "requirements": { - "duration": "30s", - "format": ["MP4"], - "resolution": ["1920x1080", "1280x720"] - } - }, - { - "item_type": "individual", - "asset_id": "end_card_image", - "asset_type": "image", - "asset_role": "end_card", - "required": false, - "requirements": { - "dimensions": "1920x1080", - "format": ["PNG", "JPG"] - } - }, - { - "item_type": "individual", - "asset_id": "companion_banner", - "asset_type": "image", - "asset_role": "companion", - "required": false, - "requirements": { - "dimensions": "300x250", - "format": ["PNG", "JPG", "GIF"] - } - }, - { - "item_type": "individual", - "asset_id": "impression_tracker", - "asset_type": "url", - "asset_role": "third_party_tracking", - "required": false, - "requirements": { - "description": "Third-party impression tracking pixel URL" - } - } - ], - - // DEPRECATED: Use "assets" above instead. Kept for backward compatibility. "assets_required": [ { "item_type": "individual", "asset_id": "video_file", "asset_type": "video", "asset_role": "hero_video", + "required": true, "requirements": { "duration": "30s", "format": ["MP4"], @@ -290,24 +243,10 @@ Formats are JSON objects with the following key fields: - **format_id**: Unique identifier (may be namespaced with `domain:id`) - **agent_url**: The creative agent authoritative for this format - **type**: Category (video, display, audio, native, dooh, rich_media) -- **assets**: Array of all asset specifications with `required` boolean indicating mandatory vs optional -- **assets_required**: *(Deprecated)* Array of required asset specifications - use `assets` instead +- **assets_required**: Array of asset specifications - **asset_role**: Identifies asset purpose (hero_image, logo, cta_button, etc.) - **renders**: Array of rendered outputs with dimensions - see below -### Asset Discovery - -The `assets` array enables comprehensive asset discovery. Each asset has a `required` boolean: - -- **`required: true`** - Asset MUST be provided for a valid creative -- **`required: false`** - Asset is optional, enhances the creative when provided (e.g., companion banners, third-party tracking pixels) - -This unified approach helps creative tools and AI agents understand the full capabilities of a format, enabling richer creative experiences when optional assets are available while clearly indicating minimum requirements. - - -**Backward Compatibility**: The `assets_required` field is deprecated but still supported. Existing implementations can continue using `assets_required` for required assets only. New implementations should use `assets` with the `required` boolean on each asset for full asset discovery. - - ### Rendered Outputs and Dimensions Formats specify their rendered outputs via the `renders` array. Most formats produce a single render, but some (companion ads, adaptive formats, multi-placement) produce multiple renders: diff --git a/v2.6-rc/docs/creative/generative-creative.mdx b/dist/docs/2.5.3/creative/generative-creative.mdx similarity index 100% rename from v2.6-rc/docs/creative/generative-creative.mdx rename to dist/docs/2.5.3/creative/generative-creative.mdx diff --git a/v2.6-rc/docs/creative/implementing-creative-agents.mdx b/dist/docs/2.5.3/creative/implementing-creative-agents.mdx similarity index 97% rename from v2.6-rc/docs/creative/implementing-creative-agents.mdx rename to dist/docs/2.5.3/creative/implementing-creative-agents.mdx index abb86bb5d0..98671f1eb2 100644 --- a/v2.6-rc/docs/creative/implementing-creative-agents.mdx +++ b/dist/docs/2.5.3/creative/implementing-creative-agents.mdx @@ -100,7 +100,7 @@ Creative agents must implement these two tasks: Return all formats your agent defines. This is how buyers discover what creative formats you support. **Key responsibilities:** -- Return complete format definitions with all `assets` (both required and optional) +- Return complete format definitions with all `assets_required` - Include your `agent_url` in each format - Use proper namespacing for `format_id` values @@ -169,7 +169,7 @@ When validating manifests: When updating format definitions: -- **Additive changes** (new optional assets with `required: false` in `assets`) are safe +- **Additive changes** (new optional assets) are safe - **Breaking changes** (removing assets, changing requirements) require new format_id - Consider versioning: `youragency.com:format_name_v2` - Maintain backward compatibility when possible diff --git a/v2.6-rc/docs/creative/index.mdx b/dist/docs/2.5.3/creative/index.mdx similarity index 100% rename from v2.6-rc/docs/creative/index.mdx rename to dist/docs/2.5.3/creative/index.mdx diff --git a/v2.6-rc/docs/creative/task-reference/build_creative.mdx b/dist/docs/2.5.3/creative/task-reference/build_creative.mdx similarity index 100% rename from v2.6-rc/docs/creative/task-reference/build_creative.mdx rename to dist/docs/2.5.3/creative/task-reference/build_creative.mdx diff --git a/v2.6-rc/docs/creative/task-reference/list_creative_formats.mdx b/dist/docs/2.5.3/creative/task-reference/list_creative_formats.mdx similarity index 98% rename from v2.6-rc/docs/creative/task-reference/list_creative_formats.mdx rename to dist/docs/2.5.3/creative/task-reference/list_creative_formats.mdx index af0348f718..648c6f6792 100644 --- a/v2.6-rc/docs/creative/task-reference/list_creative_formats.mdx +++ b/dist/docs/2.5.3/creative/task-reference/list_creative_formats.mdx @@ -56,7 +56,7 @@ Formats may produce multiple rendered pieces (e.g., video + companion banner). D | Field | Description | |-------|-------------| -| `formats` | Array of full format definitions (format_id, name, type, requirements, assets, assets_required, renders) | +| `formats` | Array of full format definitions (format_id, name, type, requirements, assets_required, renders) | | `creative_agents` | Optional array of other creative agents providing additional formats | See [Format schema](https://adcontextprotocol.org/schemas/v2/core/format.json) for complete format object structure. @@ -421,8 +421,7 @@ Each format includes: | `name` | Human-readable format name | | `type` | Format type (audio, video, display, dooh) | | `requirements` | Technical requirements (duration, file types, bitrate, etc.) | -| `assets` | Array of all assets with `required` boolean indicating mandatory vs optional | -| `assets_required` | *(Deprecated)* Array of required assets - use `assets` instead | +| `assets_required` | Array of required assets with specifications | | `renders` | Array of rendered output pieces (dimensions, role) | ### Asset Roles diff --git a/v2.6-rc/docs/creative/task-reference/preview_creative-advanced.mdx b/dist/docs/2.5.3/creative/task-reference/preview_creative-advanced.mdx similarity index 100% rename from v2.6-rc/docs/creative/task-reference/preview_creative-advanced.mdx rename to dist/docs/2.5.3/creative/task-reference/preview_creative-advanced.mdx diff --git a/v2.6-rc/docs/creative/task-reference/preview_creative.mdx b/dist/docs/2.5.3/creative/task-reference/preview_creative.mdx similarity index 100% rename from v2.6-rc/docs/creative/task-reference/preview_creative.mdx rename to dist/docs/2.5.3/creative/task-reference/preview_creative.mdx diff --git a/v2.6-rc/docs/creative/template-format-ids.mdx b/dist/docs/2.5.3/creative/template-format-ids.mdx similarity index 100% rename from v2.6-rc/docs/creative/template-format-ids.mdx rename to dist/docs/2.5.3/creative/template-format-ids.mdx diff --git a/v2.6-rc/docs/creative/universal-macros.mdx b/dist/docs/2.5.3/creative/universal-macros.mdx similarity index 100% rename from v2.6-rc/docs/creative/universal-macros.mdx rename to dist/docs/2.5.3/creative/universal-macros.mdx diff --git a/v2.6-rc/docs/curation/coming-soon.mdx b/dist/docs/2.5.3/curation/coming-soon.mdx similarity index 100% rename from v2.6-rc/docs/curation/coming-soon.mdx rename to dist/docs/2.5.3/curation/coming-soon.mdx diff --git a/v2.6-rc/docs/intro.mdx b/dist/docs/2.5.3/intro.mdx similarity index 90% rename from v2.6-rc/docs/intro.mdx rename to dist/docs/2.5.3/intro.mdx index a2c45f6fa3..5ee923163e 100644 --- a/v2.6-rc/docs/intro.mdx +++ b/dist/docs/2.5.3/intro.mdx @@ -7,9 +7,9 @@ keywords: [advertising automation protocol, programmatic advertising API, MCP ad -**πŸŽ‰ AdCP v2.6.0 Released** +**πŸŽ‰ AdCP v2.5.0 Released** -Asset discovery release featuring unified `assets` field in format schema for discovering both required and optional assets. Buyers and AI agents can now see ALL assets a format supports. [See what's new β†’](/docs/reference/release-notes) +Developer experience and API refinement release featuring type safety improvements, batch creative previews (5-10x faster), schema versioning (`/schemas/v2.5/`), template formats with dynamic sizing, enhanced product filtering, and inline creative updates. [See what's new β†’](/docs/reference/release-notes) Welcome to the Ad Context Protocol (AdCP) documentationβ€”the open standard for agentic advertising. @@ -40,7 +40,7 @@ All tasks and data models are defined with JSON schemas for validation and clien AdCP operates at multiple layers, providing a clean separation between the business roles, orchestration layer, and technical execution: -![Layers of the Stack](./layers-of-the-stack.png) +Layers of the AdCP Stack showing Business Principals, Orchestration, and Technical Execution layers ## The AdCP Ecosystem Layers @@ -75,7 +75,7 @@ Platforms that evaluate sellers and audiences, and execute buying strategies: MCP servers that provide: - **Signal Discovery**: Finding relevant signals (audiences, contextual, geographical, temporal) using natural language - **Signal Activation**: Pushing signals to decisioning platforms -- **Integration**: Connects signal platforms to orchestration via MCP +- **Integration**: Exposes data provider capabilities via MCP #### Sales Agent (Right, Bottom) MCP servers that provide: @@ -105,7 +105,7 @@ The technical infrastructure that: The AdCP ecosystem extends beyond media transactions to include creative production and delivery. This expanded view shows how creative agents integrate with the core AdCP components: -![AdCP Flows with Creative Agents](./adcp_flows.png) +AdCP ecosystem diagram showing Creative, Business, and Technical flows with agent integration ### Three Interconnected Flows @@ -142,8 +142,8 @@ The creative flow operates in parallel with the business and technical flows, en Each AdCP protocol operates within this ecosystem: ### 🎯 Signals Activation Protocol -- **Scope**: Works with **signal platforms** to discover and activate signals directly on **decisioning platforms** -- **Integration**: Direct integration between signal agents and decisioning platforms (DSPs, injective platforms) +- **Scope**: Works with **signal agents** to discover and activate signals directly on **decisioning platforms** +- **Integration**: Direct integration between signal agents and decisioning platforms (DSPs, SSPs, ad servers) - **Workflow**: Find signals β†’ Direct activation on target platform β†’ Ready for campaign use ### πŸ“ Curation Protocol (Coming Soon) @@ -152,7 +152,7 @@ Each AdCP protocol operates within this ecosystem: - **Workflow**: Define requirements β†’ Find inventory β†’ Package with signals ### πŸ’° Media Buy Protocol -- **Scope**: Works primarily with **decisioning platforms** (DSPs, injective platforms) +- **Scope**: Works primarily with **decisioning platforms** (DSPs, SSPs, ad servers) - **Integration**: Executes campaigns using curated inventory and activated signals - **Workflow**: Set objectives β†’ Execute buys β†’ Optimize performance @@ -240,7 +240,7 @@ Generate and optimize creative assets using AI-powered agents. AI is buying ads. Make sure it can buy yours. -If you operate a signal platform, DSP, or ad tech solution, AdCP lets AI agents discover and purchase your inventory. [Review the Protocol Specifications](/docs/signals/specification) to get started. +If you operate a data platform, DSP, or ad tech solution, AdCP lets AI agents discover and purchase your inventory. [Review the Protocol Specifications](/docs/signals/specification) to get started. ## For Advertisers & Agencies diff --git a/v2.6-rc/docs/layers-of-the-stack.png b/dist/docs/2.5.3/layers-of-the-stack.png similarity index 100% rename from v2.6-rc/docs/layers-of-the-stack.png rename to dist/docs/2.5.3/layers-of-the-stack.png diff --git a/v2.6-rc/docs/media-buy/advanced-topics/agentic-execution-engine.mdx b/dist/docs/2.5.3/media-buy/advanced-topics/agentic-execution-engine.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/advanced-topics/agentic-execution-engine.mdx rename to dist/docs/2.5.3/media-buy/advanced-topics/agentic-execution-engine.mdx diff --git a/v2.6-rc/docs/media-buy/advanced-topics/index.mdx b/dist/docs/2.5.3/media-buy/advanced-topics/index.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/advanced-topics/index.mdx rename to dist/docs/2.5.3/media-buy/advanced-topics/index.mdx diff --git a/v2.6-rc/docs/media-buy/advanced-topics/orchestrator-design.mdx b/dist/docs/2.5.3/media-buy/advanced-topics/orchestrator-design.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/advanced-topics/orchestrator-design.mdx rename to dist/docs/2.5.3/media-buy/advanced-topics/orchestrator-design.mdx diff --git a/v2.6-rc/docs/media-buy/advanced-topics/pricing-models.mdx b/dist/docs/2.5.3/media-buy/advanced-topics/pricing-models.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/advanced-topics/pricing-models.mdx rename to dist/docs/2.5.3/media-buy/advanced-topics/pricing-models.mdx diff --git a/v2.6-rc/docs/media-buy/advanced-topics/principals-and-security.mdx b/dist/docs/2.5.3/media-buy/advanced-topics/principals-and-security.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/advanced-topics/principals-and-security.mdx rename to dist/docs/2.5.3/media-buy/advanced-topics/principals-and-security.mdx diff --git a/v2.6-rc/docs/media-buy/advanced-topics/targeting.mdx b/dist/docs/2.5.3/media-buy/advanced-topics/targeting.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/advanced-topics/targeting.mdx rename to dist/docs/2.5.3/media-buy/advanced-topics/targeting.mdx diff --git a/v2.6-rc/docs/media-buy/advanced-topics/testing.mdx b/dist/docs/2.5.3/media-buy/advanced-topics/testing.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/advanced-topics/testing.mdx rename to dist/docs/2.5.3/media-buy/advanced-topics/testing.mdx diff --git a/docs/media-buy/capability-discovery/adagents.mdx b/dist/docs/2.5.3/media-buy/capability-discovery/adagents.mdx similarity index 100% rename from docs/media-buy/capability-discovery/adagents.mdx rename to dist/docs/2.5.3/media-buy/capability-discovery/adagents.mdx diff --git a/docs/media-buy/capability-discovery/authorized-properties.mdx b/dist/docs/2.5.3/media-buy/capability-discovery/authorized-properties.mdx similarity index 100% rename from docs/media-buy/capability-discovery/authorized-properties.mdx rename to dist/docs/2.5.3/media-buy/capability-discovery/authorized-properties.mdx diff --git a/v2.6-rc/docs/media-buy/capability-discovery/implementing-standard-formats.mdx b/dist/docs/2.5.3/media-buy/capability-discovery/implementing-standard-formats.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/capability-discovery/implementing-standard-formats.mdx rename to dist/docs/2.5.3/media-buy/capability-discovery/implementing-standard-formats.mdx diff --git a/v2.6-rc/docs/media-buy/capability-discovery/index.mdx b/dist/docs/2.5.3/media-buy/capability-discovery/index.mdx similarity index 93% rename from v2.6-rc/docs/media-buy/capability-discovery/index.mdx rename to dist/docs/2.5.3/media-buy/capability-discovery/index.mdx index 297984b4c1..885a0e16c9 100644 --- a/v2.6-rc/docs/media-buy/capability-discovery/index.mdx +++ b/dist/docs/2.5.3/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 -### [Understanding Authorization](/docs/governance/property/authorized-properties) πŸ” +### [Authorized Properties](/docs/media-buy/capability-discovery/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/governance/property/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/media-buy/capability-discovery/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/governance/property/adagents)** - Publisher authorization system (applies across all AdCP protocols) \ No newline at end of file +- **[adagents.json Specification](/docs/media-buy/capability-discovery/adagents)** - Publisher authorization system (applies across all AdCP protocols) \ No newline at end of file diff --git a/v2.6-rc/docs/media-buy/creatives/index.mdx b/dist/docs/2.5.3/media-buy/creatives/index.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/creatives/index.mdx rename to dist/docs/2.5.3/media-buy/creatives/index.mdx diff --git a/v2.6-rc/docs/media-buy/index.mdx b/dist/docs/2.5.3/media-buy/index.mdx similarity index 97% rename from v2.6-rc/docs/media-buy/index.mdx rename to dist/docs/2.5.3/media-buy/index.mdx index 5afaa30219..ce1c80cf19 100644 --- a/v2.6-rc/docs/media-buy/index.mdx +++ b/dist/docs/2.5.3/media-buy/index.mdx @@ -195,7 +195,7 @@ This accountability framework transforms the advertising marketplace from a seri The following diagram illustrates the complete lifecycle of a media buy in AdCP: -![Media Buying Lifecycle](./media-buying-lifecycle.png) +Media buying lifecycle diagram showing discovery, creation, delivery, and optimization stages ## Documentation Structure @@ -234,7 +234,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 [Understanding Authorization](/docs/governance/property/authorized-properties)** - Understand authorization requirements +1. **Learn [Authorized Properties](/docs/media-buy/capability-discovery/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/media-buying-lifecycle.png b/dist/docs/2.5.3/media-buy/media-buying-lifecycle.png similarity index 100% rename from v2.6-rc/docs/media-buy/media-buying-lifecycle.png rename to dist/docs/2.5.3/media-buy/media-buying-lifecycle.png diff --git a/v2.6-rc/docs/media-buy/media-buys/index.mdx b/dist/docs/2.5.3/media-buy/media-buys/index.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/media-buys/index.mdx rename to dist/docs/2.5.3/media-buy/media-buys/index.mdx diff --git a/v2.6-rc/docs/media-buy/media-buys/optimization-reporting.mdx b/dist/docs/2.5.3/media-buy/media-buys/optimization-reporting.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/media-buys/optimization-reporting.mdx rename to dist/docs/2.5.3/media-buy/media-buys/optimization-reporting.mdx diff --git a/v2.6-rc/docs/media-buy/media-buys/policy-compliance.mdx b/dist/docs/2.5.3/media-buy/media-buys/policy-compliance.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/media-buys/policy-compliance.mdx rename to dist/docs/2.5.3/media-buy/media-buys/policy-compliance.mdx diff --git a/v2.6-rc/docs/media-buy/product-discovery/brief-expectations.mdx b/dist/docs/2.5.3/media-buy/product-discovery/brief-expectations.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/product-discovery/brief-expectations.mdx rename to dist/docs/2.5.3/media-buy/product-discovery/brief-expectations.mdx diff --git a/v2.6-rc/docs/media-buy/product-discovery/example-briefs.mdx b/dist/docs/2.5.3/media-buy/product-discovery/example-briefs.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/product-discovery/example-briefs.mdx rename to dist/docs/2.5.3/media-buy/product-discovery/example-briefs.mdx diff --git a/v2.6-rc/docs/media-buy/product-discovery/index.mdx b/dist/docs/2.5.3/media-buy/product-discovery/index.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/product-discovery/index.mdx rename to dist/docs/2.5.3/media-buy/product-discovery/index.mdx diff --git a/v2.6-rc/docs/media-buy/product-discovery/media-products.mdx b/dist/docs/2.5.3/media-buy/product-discovery/media-products.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/product-discovery/media-products.mdx rename to dist/docs/2.5.3/media-buy/product-discovery/media-products.mdx diff --git a/v2.6-rc/docs/media-buy/task-reference/create_media_buy.mdx b/dist/docs/2.5.3/media-buy/task-reference/create_media_buy.mdx similarity index 97% rename from v2.6-rc/docs/media-buy/task-reference/create_media_buy.mdx rename to dist/docs/2.5.3/media-buy/task-reference/create_media_buy.mdx index fa138364b3..80001c91ba 100644 --- a/v2.6-rc/docs/media-buy/task-reference/create_media_buy.mdx +++ b/dist/docs/2.5.3/media-buy/task-reference/create_media_buy.mdx @@ -182,8 +182,8 @@ npx adcp \ | `pacing` | string | No | `"even"` (default), `"asap"`, or `"front_loaded"` | | `bid_price` | number | No | Bid price for auction pricing (required when `is_fixed` is false) | | `targeting_overlay` | TargetingOverlay | No | Additional targeting criteria (see [Targeting](/docs/media-buy/advanced-topics/targeting)) | -| `creative_assignments` | CreativeAssignment[] | No | Assign existing library creatives with optional weights and placement targeting | -| `creatives` | CreativeAsset[] | No | Upload new creative assets and assign (`creative_id` must not already exist in library) | +| `creative_ids` | string[] | No | Existing library creative IDs to assign | +| `creatives` | CreativeAsset[] | No | Full creative objects to upload and assign | ## Response @@ -550,7 +550,6 @@ Common errors and resolutions: | `TARGETING_TOO_NARROW` | Targeting yields zero inventory | Broaden geographic or audience criteria | | `POLICY_VIOLATION` | Brand/product violates policy | Review publisher's content policies | | `INVALID_PRICING_OPTION` | pricing_option_id not found | Use ID from product's `pricing_options` | -| `CREATIVE_ID_EXISTS` | Creative ID already exists in library | Use a different `creative_id`, assign existing creatives via `creative_assignments`, or update via `sync_creatives` | Example error response: @@ -1026,7 +1025,6 @@ For complete async handling patterns, see [Task Management](/docs/protocols/task - AXE segments enable advanced audience targeting - Pending states (`working`, `submitted`) are normal, not errors - Orchestrators MUST handle pending states as part of normal workflow -- **Inline creatives**: The `creatives` array creates NEW creatives only. To update existing creatives, use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives). To assign existing library creatives, use `creative_assignments` instead. ## Policy Compliance diff --git a/v2.6-rc/docs/media-buy/task-reference/get_media_buy_delivery.mdx b/dist/docs/2.5.3/media-buy/task-reference/get_media_buy_delivery.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/task-reference/get_media_buy_delivery.mdx rename to dist/docs/2.5.3/media-buy/task-reference/get_media_buy_delivery.mdx diff --git a/v2.6-rc/docs/media-buy/task-reference/get_products.mdx b/dist/docs/2.5.3/media-buy/task-reference/get_products.mdx similarity index 90% rename from v2.6-rc/docs/media-buy/task-reference/get_products.mdx rename to dist/docs/2.5.3/media-buy/task-reference/get_products.mdx index fa00a940b7..9a877d146e 100644 --- a/v2.6-rc/docs/media-buy/task-reference/get_products.mdx +++ b/dist/docs/2.5.3/media-buy/task-reference/get_products.mdx @@ -129,7 +129,6 @@ 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 @@ -173,12 +172,6 @@ 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 @@ -483,80 +476,6 @@ 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/v2.6-rc/docs/media-buy/task-reference/index.mdx b/dist/docs/2.5.3/media-buy/task-reference/index.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/task-reference/index.mdx rename to dist/docs/2.5.3/media-buy/task-reference/index.mdx diff --git a/v2.6-rc/docs/media-buy/task-reference/list_authorized_properties.mdx b/dist/docs/2.5.3/media-buy/task-reference/list_authorized_properties.mdx similarity index 99% rename from v2.6-rc/docs/media-buy/task-reference/list_authorized_properties.mdx rename to dist/docs/2.5.3/media-buy/task-reference/list_authorized_properties.mdx index 55bd3fcf87..5e7cb8ea9d 100644 --- a/v2.6-rc/docs/media-buy/task-reference/list_authorized_properties.mdx +++ b/dist/docs/2.5.3/media-buy/task-reference/list_authorized_properties.mdx @@ -498,6 +498,6 @@ After discovering authorized properties: ## Learn More -- [adagents.json Specification](/docs/governance/property/adagents) - Publisher authorization file format +- [adagents.json Specification](/docs/media-buy/capability-discovery/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/media-buy/task-reference/list_creative_formats.mdx b/dist/docs/2.5.3/media-buy/task-reference/list_creative_formats.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/task-reference/list_creative_formats.mdx rename to dist/docs/2.5.3/media-buy/task-reference/list_creative_formats.mdx diff --git a/v2.6-rc/docs/media-buy/task-reference/list_creatives.mdx b/dist/docs/2.5.3/media-buy/task-reference/list_creatives.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/task-reference/list_creatives.mdx rename to dist/docs/2.5.3/media-buy/task-reference/list_creatives.mdx diff --git a/v2.6-rc/docs/media-buy/task-reference/provide_performance_feedback.mdx b/dist/docs/2.5.3/media-buy/task-reference/provide_performance_feedback.mdx similarity index 100% rename from v2.6-rc/docs/media-buy/task-reference/provide_performance_feedback.mdx rename to dist/docs/2.5.3/media-buy/task-reference/provide_performance_feedback.mdx diff --git a/v2.6-rc/docs/media-buy/task-reference/sync_creatives.mdx b/dist/docs/2.5.3/media-buy/task-reference/sync_creatives.mdx similarity index 96% rename from v2.6-rc/docs/media-buy/task-reference/sync_creatives.mdx rename to dist/docs/2.5.3/media-buy/task-reference/sync_creatives.mdx index 30a5182714..e5133c59d2 100644 --- a/v2.6-rc/docs/media-buy/task-reference/sync_creatives.mdx +++ b/dist/docs/2.5.3/media-buy/task-reference/sync_creatives.mdx @@ -106,7 +106,7 @@ asyncio.run(main()) | `assignments` | object | No | Map of creative_id to array of package_ids for bulk assignment | | `dry_run` | boolean | No | When true, preview changes without applying them (default: false) | | `validation_mode` | string | No | Validation strictness: `"strict"` (default) or `"lenient"` | -| `delete_missing` | boolean | No | When true, creatives not in this sync are archived (default: false). Cannot delete creatives assigned to active, non-paused packages. | +| `delete_missing` | boolean | No | When true, creatives not in this sync are archived (default: false) | ### Creative Object @@ -651,11 +651,11 @@ The operation status (`completed`) means the review process finished. Individual | `PACKAGE_NOT_FOUND` | Package ID doesn't exist in media buy | Verify `package_id` from `create_media_buy` response | | `BRAND_SAFETY_VIOLATION` | Creative failed brand safety scan | Review content against publisher's brand safety guidelines | | `FORMAT_MISMATCH` | Assets don't match format requirements | Verify asset types and specifications match format definition | -| `CREATIVE_IN_ACTIVE_DELIVERY` | Creative is assigned to an active, non-paused package (blocks updates and `delete_missing` deletions) | Pause the package first, or create a new creative version | +| `DUPLICATE_CREATIVE_ID` | Creative ID already exists in different media buy | Use unique `creative_id` or sync to correct media buy | ## Best Practices -1. **Use upsert semantics** - Same `creative_id` updates existing creative rather than creating duplicates. This allows iterative creative development. Note: updates are blocked for creatives in active delivery (see #7). +1. **Use upsert semantics** - Same `creative_id` updates existing creative rather than creating duplicates. This allows iterative creative development. 2. **Validate first** - Use `dry_run: true` to catch errors before actual upload. This saves bandwidth and processing time. @@ -667,8 +667,6 @@ The operation status (`completed`) means the review process finished. Individual 6. **Check format support** - Use `list_creative_formats` to verify product supports your creative formats before uploading. -7. **Active delivery protection** - Creatives assigned to active, non-paused packages cannot be updated or deleted via `delete_missing`. Pause the package first, unassign the creative via `update_media_buy`, or create a new creative with a different `creative_id`. - ## Next Steps - [list_creative_formats](/docs/media-buy/task-reference/list_creative_formats) - Check supported formats before upload diff --git a/v2.6-rc/docs/media-buy/task-reference/update_media_buy.mdx b/dist/docs/2.5.3/media-buy/task-reference/update_media_buy.mdx similarity index 89% rename from v2.6-rc/docs/media-buy/task-reference/update_media_buy.mdx rename to dist/docs/2.5.3/media-buy/task-reference/update_media_buy.mdx index 0dd78ea270..237b6847e7 100644 --- a/v2.6-rc/docs/media-buy/task-reference/update_media_buy.mdx +++ b/dist/docs/2.5.3/media-buy/task-reference/update_media_buy.mdx @@ -149,25 +149,11 @@ asyncio.run(create_and_pause_campaign()) | `end_time` | string | No | Updated campaign end time | | `paused` | boolean | No | Pause/resume entire media buy (`true` = paused, `false` = active) | | `packages` | PackageUpdate[] | No | Package-level updates (see below) | -| `reporting_webhook` | object | No | Update reporting webhook configuration (see below) | -| `push_notification_config` | object | No | Webhook for async operation notifications | +| `creatives` | CreativeAsset[] | No | Upload and assign new creative assets inline | +| `creative_assignments` | CreativeAssignment[] | No | Update creative rotation weights and placement targeting | *Either `media_buy_id` OR `buyer_ref` is required (not both) -### Reporting Webhook Object - -Configure automated delivery reporting for this media buy: - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `url` | string | Yes | Webhook endpoint URL | -| `authentication` | object | Yes | Auth config with `schemes` and `credentials` | -| `reporting_frequency` | string | Yes | `hourly`, `daily`, or `monthly` | -| `requested_metrics` | string[] | No | Specific metrics to include (defaults to all) | -| `token` | string | No | Client token for validation (min 16 chars) | - -**Note**: `reporting_webhook` configures ongoing campaign reporting. `push_notification_config` is for async operation notifications (e.g., "notify me when this update completes"). - ### Package Update Object | Parameter | Type | Description | @@ -180,8 +166,7 @@ Configure automated delivery reporting for this media buy: | `pacing` | string | Updated pacing strategy | | `bid_price` | number | Updated bid price (auction products only) | | `targeting_overlay` | TargetingOverlay | Updated targeting restrictions | -| `creative_assignments` | CreativeAssignment[] | Replace assigned creatives with optional weights and placement targeting | -| `creatives` | CreativeAsset[] | Upload and assign new creatives inline (must not exist in library) | +| `creative_ids` | string[] | Replace assigned creatives | *Either `package_id` OR `buyer_ref` is required for each package update @@ -387,7 +372,7 @@ asyncio.run(update_targeting()) ### Replace Creatives -Swap out creative assignments for a package: +Swap out creative assets for a package: @@ -399,10 +384,7 @@ const result = await testAgent.updateMediaBuy({ media_buy_id: 'mb_12345', packages: [{ buyer_ref: 'ctv_package', - creative_assignments: [ - { creative_id: 'creative_video_v2' }, - { creative_id: 'creative_display_v2', weight: 60 } - ] + creative_ids: ['creative_video_v2', 'creative_display_v2'] }] }); @@ -433,10 +415,7 @@ async def replace_creatives(): packages=[ { 'buyer_ref': 'ctv_package', - 'creative_assignments': [ - {'creative_id': 'creative_video_v2'}, - {'creative_id': 'creative_display_v2', 'weight': 60} - ] + 'creative_ids': ['creative_video_v2', 'creative_display_v2'] } ] ) @@ -534,7 +513,6 @@ asyncio.run(update_multiple_packages()) βœ… **Can update:** - Start/end times (subject to seller approval) - Campaign status (active/paused) -- Reporting webhook configuration (URL, frequency, metrics) ❌ **Cannot update:** - Media buy ID @@ -570,7 +548,6 @@ Common errors and resolutions: | `BUDGET_INSUFFICIENT` | New budget below minimum | Increase budget amount | | `POLICY_VIOLATION` | Update violates content policy | Review policy requirements | | `INVALID_STATE` | Operation not allowed in current state | Check campaign status | -| `CREATIVE_ID_EXISTS` | Creative ID already exists in library | Use a different `creative_id`, assign existing creatives via `creative_assignments`, or update via `sync_creatives` | Example error response: @@ -619,16 +596,13 @@ Only specified fields are updated - omitted fields remain unchanged: } ``` -**Array replacement**: When updating arrays (like `creative_assignments`), provide the complete new array: +**Array replacement**: When updating arrays (like `creative_ids`), provide the complete new array: ```json { "packages": [{ "buyer_ref": "ctv_package", - "creative_assignments": [ - { "creative_id": "creative_video_v2" }, - { "creative_id": "creative_display_v2", "weight": 60 } - ] + "creative_ids": ["creative_video_v2", "creative_display_v2"] }] } ``` @@ -704,14 +678,13 @@ Check `affected_packages` in response to confirm changes were applied correctly. - Pending states (`working`, `submitted`) are normal, not errors - Orchestrators MUST handle pending states as part of normal workflow - `implementation_date` indicates when changes take effect (null if pending approval) -- **Inline creatives**: The `creatives` array creates NEW creatives only. To update existing creatives, use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives). To assign existing library creatives, use `creative_assignments` in the Package Update object. ## Next Steps After updating a media buy: 1. **Verify Changes**: Use [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) to confirm updates -2. **Upload New Creatives**: Use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives) if creative assignments changed +2. **Upload New Creatives**: Use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives) if creative_ids changed 3. **Monitor Performance**: Track impact of changes on campaign metrics 4. **Optimize Further**: Use [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) for ongoing optimization diff --git a/v2.6-rc/docs/protocols/a2a-guide.mdx b/dist/docs/2.5.3/protocols/a2a-guide.mdx similarity index 94% rename from v2.6-rc/docs/protocols/a2a-guide.mdx rename to dist/docs/2.5.3/protocols/a2a-guide.mdx index f66867dc49..8bfe9b4f54 100644 --- a/v2.6-rc/docs/protocols/a2a-guide.mdx +++ b/dist/docs/2.5.3/protocols/a2a-guide.mdx @@ -711,51 +711,32 @@ console.log('Examples:', getProductsSkill.examples); ] } ], - "extensions": [ - { - "uri": "https://adcontextprotocol.org/extensions/adcp", - "description": "AdCP media buying protocol support", - "required": false, - "params": { - "adcp_version": "2.6.0", - "protocols_supported": ["media_buy"], - "extensions_supported": ["sustainability"] - } + "extensions": { + "adcp": { + "adcp_version": "2.4.0", + "protocols_supported": ["media_buy"] } - ] + } } ``` ### AdCP Extension -**Recommended**: Include the AdCP extension in your agent card's `extensions` array to declare AdCP support programmatically. - -The A2A protocol uses an `extensions` array where each extension has: -- **`uri`**: Extension identifier (use `https://adcontextprotocol.org/extensions/adcp`) -- **`description`**: Human-readable description of how you use AdCP -- **`required`**: Whether clients must support this extension (typically `false` for AdCP) -- **`params`**: AdCP-specific configuration (see schema below) +**Recommended**: Include the `extensions.adcp` field in your agent card to declare AdCP support programmatically. ```javascript // Check if agent supports AdCP const agentCard = await fetch('https://sales.example.com/.well-known/agent.json') .then(r => r.json()); -// Find the AdCP extension in the extensions array -const adcpExt = agentCard.extensions?.find( - ext => ext.uri === 'https://adcontextprotocol.org/extensions/adcp' -); - -if (adcpExt) { - console.log('AdCP Version:', adcpExt.params.adcp_version); - console.log('Supported domains:', adcpExt.params.protocols_supported); +if (agentCard.extensions?.adcp) { + console.log('AdCP Version:', agentCard.extensions.adcp.adcp_version); + console.log('Supported domains:', agentCard.extensions.adcp.protocols_supported); // ["media_buy", "creative", "signals"] - console.log('Typed extensions:', adcpExt.params.extensions_supported); - // ["sustainability"] } ``` -**Extension Params Schema**: See the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json) for the complete `params` specification. +**Extension Structure**: See the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json) for complete specification. **Benefits**: - Clients can discover AdCP capabilities without making test calls diff --git a/v2.6-rc/docs/protocols/a2a-response-format.mdx b/dist/docs/2.5.3/protocols/a2a-response-format.mdx similarity index 100% rename from v2.6-rc/docs/protocols/a2a-response-format.mdx rename to dist/docs/2.5.3/protocols/a2a-response-format.mdx diff --git a/v2.6-rc/docs/protocols/context-management.mdx b/dist/docs/2.5.3/protocols/context-management.mdx similarity index 92% rename from v2.6-rc/docs/protocols/context-management.mdx rename to dist/docs/2.5.3/protocols/context-management.mdx index 5c623bfc4a..8ba34f6505 100644 --- a/v2.6-rc/docs/protocols/context-management.mdx +++ b/dist/docs/2.5.3/protocols/context-management.mdx @@ -169,9 +169,9 @@ Response (application-level context is repeated inside the payload): } ``` -![Application Context Lifecycle 01](./initator-context-lifecycle-01.png) +Application context lifecycle diagram showing initiator request flow -![Application Context Lifecycle 02](./initator-context-lifecycle-02.png) +Application context lifecycle diagram showing webhook response flow Webhook updates include the same application-level `context` inside the webhook `result` payload. diff --git a/v2.6-rc/docs/protocols/core-concepts.mdx b/dist/docs/2.5.3/protocols/core-concepts.mdx similarity index 100% rename from v2.6-rc/docs/protocols/core-concepts.mdx rename to dist/docs/2.5.3/protocols/core-concepts.mdx diff --git a/v2.6-rc/docs/protocols/envelope-examples.mdx b/dist/docs/2.5.3/protocols/envelope-examples.mdx similarity index 100% rename from v2.6-rc/docs/protocols/envelope-examples.mdx rename to dist/docs/2.5.3/protocols/envelope-examples.mdx diff --git a/v2.6-rc/docs/protocols/error-handling.mdx b/dist/docs/2.5.3/protocols/error-handling.mdx similarity index 100% rename from v2.6-rc/docs/protocols/error-handling.mdx rename to dist/docs/2.5.3/protocols/error-handling.mdx diff --git a/v2.6-rc/docs/protocols/getting-started.mdx b/dist/docs/2.5.3/protocols/getting-started.mdx similarity index 100% rename from v2.6-rc/docs/protocols/getting-started.mdx rename to dist/docs/2.5.3/protocols/getting-started.mdx diff --git a/v2.6-rc/docs/protocols/initator-context-lifecycle-01.png b/dist/docs/2.5.3/protocols/initator-context-lifecycle-01.png similarity index 100% rename from v2.6-rc/docs/protocols/initator-context-lifecycle-01.png rename to dist/docs/2.5.3/protocols/initator-context-lifecycle-01.png diff --git a/v2.6-rc/docs/protocols/initator-context-lifecycle-02.png b/dist/docs/2.5.3/protocols/initator-context-lifecycle-02.png similarity index 100% rename from v2.6-rc/docs/protocols/initator-context-lifecycle-02.png rename to dist/docs/2.5.3/protocols/initator-context-lifecycle-02.png diff --git a/v2.6-rc/docs/protocols/mcp-guide.mdx b/dist/docs/2.5.3/protocols/mcp-guide.mdx similarity index 91% rename from v2.6-rc/docs/protocols/mcp-guide.mdx rename to dist/docs/2.5.3/protocols/mcp-guide.mdx index a8581b776e..c3303454dd 100644 --- a/v2.6-rc/docs/protocols/mcp-guide.mdx +++ b/dist/docs/2.5.3/protocols/mcp-guide.mdx @@ -632,60 +632,22 @@ const adcpTools = tools.filter(t => t.name.startsWith('adcp_') || ['get_products', 'create_media_buy'].includes(t.name)); ``` -### AdCP Extension via MCP Server Card +### AdCP Extension (Future) -MCP servers can declare AdCP support via a server card at `/.well-known/mcp.json` (or `/.well-known/server.json`). AdCP-specific metadata goes in the `_meta` field using the `adcontextprotocol.org` namespace. +**Status**: MCP server cards are expected in a future MCP release. When available, AdCP servers will include the AdCP extension. ```json { - "name": "io.adcontextprotocol/media-buy-agent", - "version": "1.0.0", - "title": "AdCP Media Buy Agent", - "description": "AI-powered media buying agent implementing AdCP", - "tools": [ - { "name": "get_products" }, - { "name": "create_media_buy" }, - { "name": "list_creative_formats" } - ], - "_meta": { - "adcontextprotocol.org": { - "adcp_version": "2.6.0", - "protocols_supported": ["media_buy"], - "extensions_supported": ["sustainability"] + "extensions": { + "adcp": { + "adcp_version": "2.4.0", + "protocols_supported": ["media_buy", "creative", "signals"] } } } ``` -**Discovering AdCP support:** - -```javascript -// Check both possible locations for MCP server card -const serverCard = await fetch('https://sales.example.com/.well-known/mcp.json') - .then(r => r.ok ? r.json() : null) - .catch(() => null) - || await fetch('https://sales.example.com/.well-known/server.json') - .then(r => r.json()); - -// Check for AdCP metadata -const adcpMeta = serverCard?._meta?.['adcontextprotocol.org']; - -if (adcpMeta) { - console.log('AdCP Version:', adcpMeta.adcp_version); - console.log('Supported domains:', adcpMeta.protocols_supported); - // ["media_buy", "creative", "signals"] - console.log('Typed extensions:', adcpMeta.extensions_supported); - // ["sustainability"] -} -``` - -**Benefits:** -- Clients can discover AdCP capabilities without making test calls -- Declare which protocol domains you implement (media_buy, creative, signals) -- Declare which [typed extensions](/docs/reference/extensions-and-context#typed-extensions) you support -- Enable compatibility checks based on version - -**Note:** The `_meta` field uses reverse DNS namespacing per the [MCP server.json spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/generic-server-json.md). AdCP servers should support both `/.well-known/mcp.json` and `/.well-known/server.json` locations. +This will allow clients to programmatically discover which AdCP version and protocol domains an MCP server implements. See the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json) for specification details. ### Parameter Validation ```javascript diff --git a/v2.6-rc/docs/protocols/protocol-comparison.mdx b/dist/docs/2.5.3/protocols/protocol-comparison.mdx similarity index 100% rename from v2.6-rc/docs/protocols/protocol-comparison.mdx rename to dist/docs/2.5.3/protocols/protocol-comparison.mdx diff --git a/v2.6-rc/docs/protocols/task-management.mdx b/dist/docs/2.5.3/protocols/task-management.mdx similarity index 100% rename from v2.6-rc/docs/protocols/task-management.mdx rename to dist/docs/2.5.3/protocols/task-management.mdx diff --git a/v2.6-rc/docs/quickstart.mdx b/dist/docs/2.5.3/quickstart.mdx similarity index 100% rename from v2.6-rc/docs/quickstart.mdx rename to dist/docs/2.5.3/quickstart.mdx diff --git a/v2.6-rc/docs/reference/ad-tech-primer.mdx b/dist/docs/2.5.3/reference/ad-tech-primer.mdx similarity index 100% rename from v2.6-rc/docs/reference/ad-tech-primer.mdx rename to dist/docs/2.5.3/reference/ad-tech-primer.mdx diff --git a/v2.6-rc/docs/reference/adcp-ecosystem.mdx b/dist/docs/2.5.3/reference/adcp-ecosystem.mdx similarity index 100% rename from v2.6-rc/docs/reference/adcp-ecosystem.mdx rename to dist/docs/2.5.3/reference/adcp-ecosystem.mdx diff --git a/v2.6-rc/docs/reference/authentication.mdx b/dist/docs/2.5.3/reference/authentication.mdx similarity index 100% rename from v2.6-rc/docs/reference/authentication.mdx rename to dist/docs/2.5.3/reference/authentication.mdx diff --git a/v2.6-rc/docs/reference/changelog.mdx b/dist/docs/2.5.3/reference/changelog.mdx similarity index 100% rename from v2.6-rc/docs/reference/changelog.mdx rename to dist/docs/2.5.3/reference/changelog.mdx diff --git a/v2.6-rc/docs/reference/creative-quick-reference.mdx b/dist/docs/2.5.3/reference/creative-quick-reference.mdx similarity index 100% rename from v2.6-rc/docs/reference/creative-quick-reference.mdx rename to dist/docs/2.5.3/reference/creative-quick-reference.mdx diff --git a/v2.6-rc/docs/reference/data-models.mdx b/dist/docs/2.5.3/reference/data-models.mdx similarity index 100% rename from v2.6-rc/docs/reference/data-models.mdx rename to dist/docs/2.5.3/reference/data-models.mdx diff --git a/v2.6-rc/docs/reference/error-codes.mdx b/dist/docs/2.5.3/reference/error-codes.mdx similarity index 89% rename from v2.6-rc/docs/reference/error-codes.mdx rename to dist/docs/2.5.3/reference/error-codes.mdx index 0eac0041c3..0132e64cdf 100644 --- a/v2.6-rc/docs/reference/error-codes.mdx +++ b/dist/docs/2.5.3/reference/error-codes.mdx @@ -425,61 +425,6 @@ Request exceeded maximum processing time. **Resolution**: Refine request parameters or retry. -## Content Standards Errors - -### STANDARDS_NOT_FOUND -Specified standards ID doesn't exist. - -**Example**: -```json -{ - "$schema": "https://adcontextprotocol.org/schemas/v2/core/error.json", - "code": "STANDARDS_NOT_FOUND", - "message": "No standards found with ID 'invalid_id'", - "details": { - "standards_id": "invalid_id" - } -} -``` - -**Resolution**: Use `list_content_standards` to find valid standards IDs. - -### STANDARDS_IN_USE -Cannot delete standards that are referenced by active media buys. - -**Example**: -```json -{ - "$schema": "https://adcontextprotocol.org/schemas/v2/core/error.json", - "code": "STANDARDS_IN_USE", - "message": "Cannot delete standards 'nike_emea_safety' - currently referenced by active media buys", - "details": { - "standards_id": "nike_emea_safety", - "active_media_buy_count": 3 - } -} -``` - -**Resolution**: Wait for media buys to complete before deleting. - -### STANDARDS_SCOPE_CONFLICT -New standards configuration conflicts with existing standards for the same scope. - -**Example**: -```json -{ - "$schema": "https://adcontextprotocol.org/schemas/v2/core/error.json", - "code": "STANDARDS_SCOPE_CONFLICT", - "message": "Standards already exist for brand 'nike' in countries ['GB', 'DE']", - "details": { - "conflicting_standards_id": "nike_emea_safety", - "overlapping_countries": ["GB", "DE"] - } -} -``` - -**Resolution**: Update existing standards or narrow scope to avoid overlap. - ## Data Errors ### DATA_QUALITY_ISSUE @@ -561,10 +506,7 @@ const PERMANENT_ERRORS = [ 'INSUFFICIENT_PERMISSIONS', 'SEGMENT_NOT_FOUND', 'PLATFORM_UNAUTHORIZED', - 'UNSUPPORTED_VERSION', - 'STANDARDS_NOT_FOUND', - 'STANDARDS_IN_USE', - 'STANDARDS_SCOPE_CONFLICT' + 'UNSUPPORTED_VERSION' ]; function isRetryable(errorCode: string): boolean { diff --git a/v2.6-rc/docs/reference/extensions-and-context.mdx b/dist/docs/2.5.3/reference/extensions-and-context.mdx similarity index 56% rename from v2.6-rc/docs/reference/extensions-and-context.mdx rename to dist/docs/2.5.3/reference/extensions-and-context.mdx index e29ec563c3..48484c0201 100644 --- a/v2.6-rc/docs/reference/extensions-and-context.mdx +++ b/dist/docs/2.5.3/reference/extensions-and-context.mdx @@ -181,213 +181,6 @@ Without namespacing: - Impossible to support multiple platforms simultaneously - Harder to deprecate platform-specific features -## Typed Extensions - -AdCP supports formal JSON schemas for extensions, enabling type safety and validation. Typed extensions are published in the `/schemas/extensions/` directory and can be declared in agent cards. - -### How Typed Extensions Work - -Typed extensions provide a middle ground between untyped vendor extensions and core AdCP fields: - -- **Untyped extensions** (`ext.vendor.*`) - Any JSON, no validation, vendor-specific -- **Typed extensions** (`ext.sustainability.*`) - Formal schema, validated, cross-vendor -- **Core fields** - Part of AdCP specification, required for interoperability - -Typed extensions enable SDK code generation, schema validation, and cross-vendor interoperability while remaining optional. - -### Extension Schema Registry - -The registry of available typed extensions is auto-generated from extension files: -- **Registry**: `/schemas/v1/extensions/index.json` -- **Extension schemas**: `/schemas/v1/extensions/{namespace}.json` -- **Meta schema**: `/schemas/v1/extensions/extension-meta.json` - -Each versioned schema path (`/schemas/v2.5/`, `/schemas/v2.6/`) includes only extensions valid for that version, based on the `valid_from` and `valid_until` fields in each extension. - -### Extension Versioning - -Extensions are versioned independently of the AdCP specification: - -```json -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "/schemas/extensions/sustainability.json", - "title": "Sustainability Extension", - "description": "Carbon footprint and green certification data", - "valid_from": "2.5", - "valid_until": "4.0", - "docs_url": "https://adcontextprotocol.org/docs/extensions/sustainability", - "type": "object", - "properties": { - "carbon_kg_per_impression": { "type": "number" }, - "certified_green": { "type": "boolean" } - } -} -``` - -- **`valid_from`** (required): Minimum AdCP version this extension works with (e.g., `"2.5"`) -- **`valid_until`** (optional): Last AdCP version this extension works with. Omit if still valid. - -This allows extensions to be backported to earlier AdCP versions and deprecated gracefully. - -### Declaring Extension Support - -Agents declare which typed extensions they support in their discovery metadata. The location depends on the protocol: - -#### A2A Agent Cards - -A2A agents declare AdCP support in the `extensions` array at `/.well-known/agent.json`: - -```json -{ - "name": "AdCP Media Buy Agent", - "skills": [{ "name": "get_products" }, { "name": "create_media_buy" }], - "extensions": [ - { - "uri": "https://adcontextprotocol.org/extensions/adcp", - "description": "AdCP media buying protocol support", - "required": false, - "params": { - "adcp_version": "2.6.0", - "protocols_supported": ["media_buy"], - "extensions_supported": ["sustainability"] - } - } - ] -} -``` - -See the [A2A Guide](/docs/protocols/a2a-guide#adcp-extension) for complete details. - -#### MCP Server Cards - -MCP servers declare AdCP support via the `_meta` field at `/.well-known/mcp.json` or `/.well-known/server.json`: - -```json -{ - "name": "io.adcontextprotocol/media-buy-agent", - "tools": [{ "name": "get_products" }, { "name": "create_media_buy" }], - "_meta": { - "adcontextprotocol.org": { - "adcp_version": "2.6.0", - "protocols_supported": ["media_buy"], - "extensions_supported": ["sustainability"] - } - } -} -``` - -See the [MCP Guide](/docs/protocols/mcp-guide#adcp-extension-via-mcp-server-card) for complete details. - -#### Extension Params Schema - -Both protocols use the same params structure, defined in the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json): -- **`adcp_version`** (required): Semantic version of AdCP implemented (e.g., "2.6.0") -- **`protocols_supported`** (required): Array of protocol domains (media_buy, creative, signals) -- **`extensions_supported`** (optional): Array of typed extension namespaces - -When an agent declares support for an extension, clients can expect: -- The agent will accept and populate `ext.{namespace}` fields conforming to that extension's schema -- Data in `ext.{namespace}` is strongly typed and can be validated -- SDK code generation can produce type-safe interfaces for the extension - -### Using Typed Extensions - -When an agent supports a typed extension, you can include and expect data in the corresponding namespace: - -```json -{ - "product_id": "premium_ctv", - "name": "Premium CTV Package", - "ext": { - "sustainability": { - "carbon_kg_per_impression": 0.0012, - "certified_green": true - } - } -} -``` - -### Creating a Typed Extension - -To create a typed extension: - -1. **Create the extension file** at `/schemas/extensions/{namespace}.json` -2. **Follow the meta schema** defined in `/schemas/extensions/extension-meta.json` -3. **Set `valid_from`** to the minimum AdCP version your extension requires -4. **Add documentation** via the `docs_url` field - -Example extension file: - -```json -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "/schemas/extensions/sustainability.json", - "title": "Sustainability Extension", - "description": "Carbon footprint and green certification data for ad products", - "valid_from": "2.5", - "docs_url": "https://adcontextprotocol.org/docs/extensions/sustainability", - "type": "object", - "properties": { - "carbon_kg_per_impression": { - "type": "number", - "description": "Estimated CO2 emissions per impression in kilograms" - }, - "certified_green": { - "type": "boolean", - "description": "Whether the inventory is certified by a green advertising program" - }, - "certification_provider": { - "type": "string", - "description": "Name of the green certification provider" - } - } -} -``` - -The build process auto-discovers extension files and includes them in the appropriate versioned schema builds. - -### Proposing Typed Extensions - -To propose a typed extension for inclusion in AdCP: - -1. **Validate in production first** - Use vendor-namespaced extensions (`ext.yourcompany.*`) to prove value -2. **Document the schema** - Define clear property names, types, and descriptions -3. **Gather adoption evidence** - Show multiple implementations or strong demand -4. **Submit an RFC** - Propose the extension with schema and use cases -5. **If accepted** - Extension is added to `/schemas/extensions/` with appropriate `valid_from` - -### Reserved Namespaces - -The following namespaces are reserved and cannot be used for typed extensions: -- `adcp`, `core`, `protocol`, `schema`, `meta`, `ext`, `context` - -These are reserved to prevent confusion with core AdCP concepts. - -### Extension Deprecation Policy - -#### When Extensions May Be Deprecated - -1. **Superseded** - A better extension exists with a documented migration path -2. **Promoted to core** - The extension becomes a core AdCP field -3. **Low adoption** - Insufficient usage across implementations -4. **Incompatible** - Breaking changes in AdCP require removal - -#### Deprecation Process - -1. Set `valid_until` to current version + 1 minor release (minimum grace period) -2. Announce deprecation in CHANGELOG -3. Publish migration guide if replacement exists -4. Add deprecation warning to extension docs -5. Extension removed from builds after `valid_until` version - -#### Emergency Deprecation - -Security issues may require immediate deprecation: -- `valid_until` set to current version -- Security advisory published -- Patch release issued - ## Proposing Spec Additions If your extension field represents **common ad tech functionality** that would benefit all AdCP implementations: diff --git a/v2.6-rc/docs/reference/glossary.mdx b/dist/docs/2.5.3/reference/glossary.mdx similarity index 89% rename from v2.6-rc/docs/reference/glossary.mdx rename to dist/docs/2.5.3/reference/glossary.mdx index e069d90d63..dbe1fec44a 100644 --- a/v2.6-rc/docs/reference/glossary.mdx +++ b/dist/docs/2.5.3/reference/glossary.mdx @@ -67,11 +67,14 @@ Size unit representing unique device identifiers (cookies, mobile IDs) - typical **Device Type** (Media Buy Protocol) Targeting dimension for platform types: mobile, desktop, tablet, CTV, audio, DOOH. +**Decisioning Platform** +Technical infrastructure that selects which ad to serve at impression time. Receives activated signals and executes campaigns. Examples: DSPs (The Trade Desk), SSPs (Index Exchange, OpenX, PubMatic), ad servers (Google Ad Manager, Kevel). + **DOOH (Digital Out-of-Home)** Digital advertising displayed on screens in public spaces such as billboards, transit stations, airports, and retail locations. Uses CPM or flat_rate pricing with parameters for SOV, duration, and venue targeting. **DSP (Demand-Side Platform)** -Technology platform that allows advertisers to buy advertising inventory programmatically. +A type of decisioning platform that allows advertisers to buy advertising inventory programmatically. ## E @@ -163,11 +166,14 @@ Pricing model based on a percentage of media spend rather than fixed CPM. ## S +**Sales Agent** +An MCP server that exposes publisher inventory for discovery and purchase. Handles product discovery, media buy creation, and campaign management. Examples: Publisher ad servers exposing AdCP interfaces, sales house platforms. + **Screen Time** Total duration an ad was displayed across all DOOH screens, measured in seconds. Used for DOOH delivery reporting. **Seat** -A specific advertising account within a platform, typically representing a brand or campaign. +A specific advertising account within a decisioning platform, typically representing a brand or campaign. **Segment ID** The specific identifier used for signal activation, may differ from signal_id. @@ -175,9 +181,15 @@ The specific identifier used for signal activation, may differ from signal_id. **Share of Voice (SOV)** Percentage of available ad inventory allocated to a specific advertiser in DOOH loops. Expressed as 0.0-1.0 (e.g., 0.15 = 15% SOV). +**Signal Agent** +An MCP server that provides signal discovery and activation services. Enables natural language audience discovery and deploys signals to decisioning platforms. Can be private (owned by a single principal) or marketplace (licensing data to multiple principals). Examples: LiveRamp, Experian, Peer39. + **Size Unit** (Signals Protocol) The measurement type for signal size: individuals, devices, or households. +**SSP (Supply-Side Platform)** +A type of decisioning platform that helps publishers sell advertising inventory programmatically. SSPs connect to multiple demand sources and make ad selection decisions. Examples: Index Exchange, OpenX, PubMatic, Magnite. + ## T **Takeover** diff --git a/v2.6-rc/docs/reference/gmsf-reference.mdx b/dist/docs/2.5.3/reference/gmsf-reference.mdx similarity index 100% rename from v2.6-rc/docs/reference/gmsf-reference.mdx rename to dist/docs/2.5.3/reference/gmsf-reference.mdx diff --git a/v2.6-rc/docs/reference/implementor-faq.mdx b/dist/docs/2.5.3/reference/implementor-faq.mdx similarity index 98% rename from v2.6-rc/docs/reference/implementor-faq.mdx rename to dist/docs/2.5.3/reference/implementor-faq.mdx index 747ada9c0c..ce93b224df 100644 --- a/v2.6-rc/docs/reference/implementor-faq.mdx +++ b/dist/docs/2.5.3/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/governance/property/adagents#buyer-agent-validation) for complete requirements. +See [Authorization Validation](/docs/media-buy/capability-discovery/adagents#buyer-agent-validation) for complete requirements. ## Terminology diff --git a/v2.6-rc/docs/reference/media-buy-quick-reference.mdx b/dist/docs/2.5.3/reference/media-buy-quick-reference.mdx similarity index 100% rename from v2.6-rc/docs/reference/media-buy-quick-reference.mdx rename to dist/docs/2.5.3/reference/media-buy-quick-reference.mdx diff --git a/v2.6-rc/docs/reference/release-notes.mdx b/dist/docs/2.5.3/reference/release-notes.mdx similarity index 100% rename from v2.6-rc/docs/reference/release-notes.mdx rename to dist/docs/2.5.3/reference/release-notes.mdx diff --git a/v2.6-rc/docs/reference/roadmap.mdx b/dist/docs/2.5.3/reference/roadmap.mdx similarity index 94% rename from v2.6-rc/docs/reference/roadmap.mdx rename to dist/docs/2.5.3/reference/roadmap.mdx index 137ddeb29e..35b1ec2d61 100644 --- a/v2.6-rc/docs/reference/roadmap.mdx +++ b/dist/docs/2.5.3/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 [3.0] +### Governance Protocol -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. +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. ### Private Marketplace (PMP) Support - Deal ID integration diff --git a/v2.6-rc/docs/reference/schema-versioning.mdx b/dist/docs/2.5.3/reference/schema-versioning.mdx similarity index 93% rename from v2.6-rc/docs/reference/schema-versioning.mdx rename to dist/docs/2.5.3/reference/schema-versioning.mdx index 7b979d3ffd..8c11d4606e 100644 --- a/v2.6-rc/docs/reference/schema-versioning.mdx +++ b/dist/docs/2.5.3/reference/schema-versioning.mdx @@ -21,7 +21,7 @@ All AdCP schemas are available at multiple paths: ### Exact Version (Recommended for Production) ``` -/schemas/2.6.0/core/product.json +/schemas/2.5.0/core/product.json ``` - **Pin to specific release** - URL never changes - **Guaranteed stability** - Schema won't update unexpectedly @@ -69,8 +69,8 @@ Bundled schemas are experimental and may change or be removed in future releases For tools that don't support `$ref` resolution (some API clients, code generators, or desktop applications), AdCP provides **bundled schemas** with all references resolved inline: ``` -/schemas/2.6.0/bundled/media-buy/create-media-buy-request.json -/schemas/2.6.0/bundled/media-buy/get-products-response.json +/schemas/2.5.0/bundled/media-buy/create-media-buy-request.json +/schemas/2.5.0/bundled/media-buy/get-products-response.json ``` ### What's Bundled @@ -91,11 +91,11 @@ Bundled schemas are generated for all request/response schemas (`*-request.json` ```javascript // Bundled - self-contained, no ref resolution needed -const schema = await fetch('https://adcp.org/schemas/2.6.0/bundled/media-buy/create-media-buy-request.json'); +const schema = await fetch('https://adcp.org/schemas/2.5.0/bundled/media-buy/create-media-buy-request.json'); const validate = ajv.compile(await schema.json()); // Modular - requires $ref resolution -const schema = await fetch('https://adcp.org/schemas/2.6.0/media-buy/create-media-buy-request.json'); +const schema = await fetch('https://adcp.org/schemas/2.5.0/media-buy/create-media-buy-request.json'); // This schema contains $ref to other schemas that must be resolved ``` @@ -105,7 +105,7 @@ Bundled schemas include a `_bundled` field with generation metadata: ```json { - "$id": "/schemas/2.6.0/bundled/core/product.json", + "$id": "/schemas/2.5.0/bundled/core/product.json", "title": "Product", "_bundled": { "generatedAt": "2024-12-17T10:30:00.000Z", @@ -120,7 +120,7 @@ Bundled schemas include a `_bundled` field with generation metadata: ```javascript // Production - pin exact version -const SCHEMA_VERSION = '2.6.0'; +const SCHEMA_VERSION = '2.5.0'; const schema = await fetch(`https://adcp.org/schemas/${SCHEMA_VERSION}/core/product.json`); // Stable development - track minor version (patch updates only) @@ -137,12 +137,12 @@ Always use exact versions to ensure generated types match your target protocol v ```bash # TypeScript SDK generation npx json-schema-to-typescript \ - https://adcp.org/schemas/2.6.0/core/product.json \ + https://adcp.org/schemas/2.5.0/core/product.json \ --output types/product.d.ts # Python SDK generation datamodel-codegen \ - --url https://adcp.org/schemas/2.6.0/core/product.json \ + --url https://adcp.org/schemas/2.5.0/core/product.json \ --output models/product.py ``` @@ -153,7 +153,7 @@ datamodel-codegen \ import Ajv from 'ajv'; const ajv = new Ajv(); -const schema = await fetch('https://adcp.org/schemas/2.6.0/core/product.json'); +const schema = await fetch('https://adcp.org/schemas/2.5.0/core/product.json'); const validate = ajv.compile(await schema.json()); if (!validate(data)) { @@ -227,7 +227,7 @@ If you're currently using `/schemas/v2/`: ``` 3. **Consider pinning** for production stability: ```javascript - const schema = await fetch('/schemas/2.6.0/core/product.json'); + const schema = await fetch('/schemas/2.5.0/core/product.json'); ``` ### From Exact Version to Major Alias @@ -270,11 +270,11 @@ If you want to track the latest 2.x: ```bash # Get schema registry with version info curl https://adcp.org/schemas/v2/index.json | jq '.adcp_version' -# Output: "2.6.0" +# Output: "2.5.0" # Get version from specific release -curl https://adcp.org/schemas/2.6.0/index.json | jq '.adcp_version' -# Output: "2.6.0" +curl https://adcp.org/schemas/2.5.0/index.json | jq '.adcp_version' +# Output: "2.5.0" ``` ### List Available Versions diff --git a/v2.6-rc/docs/reference/security.mdx b/dist/docs/2.5.3/reference/security.mdx similarity index 89% rename from v2.6-rc/docs/reference/security.mdx rename to dist/docs/2.5.3/reference/security.mdx index 44d946041f..74b01e63de 100644 --- a/v2.6-rc/docs/reference/security.mdx +++ b/dist/docs/2.5.3/reference/security.mdx @@ -21,24 +21,84 @@ AdCP operates in a high-stakes environment where: This document provides security requirements and best practices for AdCP implementations. +## Threat Model + +Understanding the specific threats that AdCP implementations face helps choose appropriate mitigations. Different authentication mechanisms address different threats. + +### Threat Categories + +| Threat | Description | Primary Mitigations | +|--------|-------------|---------------------| +| **Credential Theft** | Attacker obtains valid tokens through phishing, malware, or database breach | Short token expiry, credential rotation, anomaly detection | +| **Token Replay** | Attacker intercepts and reuses a valid token | Short expiry, request signing, one-time-use tokens | +| **Domain Hijacking** | Attacker takes control of a domain (expiry, DNS hijack, BGP hijack) and receives tokens intended for legitimate server | Certificate pinning, domain monitoring, out-of-band verification | +| **Man-in-the-Middle** | Attacker intercepts traffic between client and server | TLS, certificate pinning, mTLS | +| **Privilege Escalation** | Authenticated principal accesses another principal's data | Principal isolation, row-level security, authorization checks | +| **Budget Fraud** | Attacker creates unauthorized financial commitments | Approval workflows, spending limits, anomaly detection | + +### Domain Trust Assumption + +AdCP, like most APIs, operates on the assumption that the domain you're connecting to is controlled by the intended party. This is the **domain trust assumption**. + + +**Critical**: OAuth 2.0 does not protect against domain hijacking. If an attacker controls `api.adplatform.com`, they can complete OAuth authorization flows just as easily as they can accept bearer tokens. The client trusts the domain, regardless of authentication mechanism. + + +**What OAuth provides:** +- Delegated access (user authorizes agent to act on their behalf) +- Scoped permissions (limit what the token can do) +- Token revocation without rotating credentials +- Separation of authentication from authorization + +**What OAuth does NOT provide:** +- Protection against compromised domains +- Verification that the server is the intended recipient +- Protection against man-in-the-middle attacks (beyond what TLS provides) + +**Mitigations for domain trust threats:** + +| Mitigation | How It Helps | +|------------|--------------| +| **TLS Certificate Pinning** | Rejects connections even if attacker has valid cert for the domain | +| **DNSSEC** | Protects against DNS spoofing | +| **Domain Monitoring** | Alerts when domain registration changes or expires | +| **Out-of-Band Verification** | Verify platform identity through secondary channel before large commitments | +| **ads.txt/sellers.json** | Industry-standard publisher authorization verification | + +### Authentication Mechanism Selection + +Choose authentication based on the threats you need to address: + +| Scenario | Recommended Auth | Rationale | +|----------|------------------|-----------| +| Server-to-server integration | API keys with IP allowlist | Simple, server identity verified by IP | +| Agent acting on user's behalf | OAuth 2.0 with PKCE | User controls delegation, scoped permissions | +| High-value financial operations | Request signing + short-lived tokens | Integrity verification beyond auth | +| Zero-trust environment | mTLS | Cryptographic verification of both parties | + ## Risk Classification AdCP operations fall into three risk categories based on their potential impact: ### High-Risk Operations (Financial) -These operations commit real advertising budgets and require the strongest authentication: +These operations commit real advertising budgets and require strong controls: -| Operation | Risk | Recommended Auth | -|-----------|------|------------------| -| `create_media_buy` | Creates financial commitments | OAuth 2.0 or mTLS | -| `update_media_buy` | Modifies budgets and campaign parameters | OAuth 2.0 or mTLS | +| Operation | Risk | Primary Threat | +|-----------|------|----------------| +| `create_media_buy` | Creates financial commitments | Budget fraud, credential theft | +| `update_media_buy` | Modifies budgets and campaign parameters | Unauthorized modifications | **Requirements for High-Risk Operations**: -- Implementations SHOULD use OAuth 2.0 with proper token validation or mutual TLS -- Bearer tokens alone provide weaker security guarantees (see [Bearer Token Risks](#bearer-token-risks)) -- Multi-factor authentication or approval workflows recommended for large budgets -- All requests must be logged with full audit trail +- Short-lived credentials (tokens expiring in ≀15 minutes) +- Request signing for transaction integrity (see [Request Signing](#request-signing-for-financial-operations)) +- Multi-factor authentication or approval workflows for large budgets +- Spending limits and anomaly detection +- Full audit trail with immutable logging + + +**Authentication mechanism alone does not determine security level.** A properly implemented bearer token system with short expiry, request signing, and spending limits provides stronger protection than a long-lived OAuth token without additional controls. + ### Medium-Risk Operations (Data Access) @@ -196,30 +256,38 @@ function verifyWebhookSignature( ### Bearer Token Risks -While bearer tokens (including JWT) are widely used across the industry, they carry specific risks that implementations should understand, particularly for [high-risk financial operations](#high-risk-operations-financial): +Bearer tokens are the industry standard for API authentication (Google Ads, Meta Marketing API, The Trade Desk, etc.). Understanding their specific risks helps implement appropriate mitigations. See [Threat Model](#threat-model) for context. + +**Inherent Risks of Bearer Tokens**: -**Token Interception Risks**: -- **Domain Hijacking**: If a domain expires or is compromised, attackers could receive valid bearer tokens intended for legitimate endpoints. Unlike OAuth flows, there's no bilateral verification that the server is the intended recipient. -- **Man-in-the-Middle**: Without certificate pinning, compromised certificate authorities could enable token interception even over HTTPS. -- **Token Replay**: Stolen tokens can be replayed until expiration. +| Risk | Description | Impact | +|------|-------------|--------| +| **Token Replay** | Stolen tokens can be reused until expiration | Unauthorized access for token lifetime | +| **Credential Theft** | Tokens extracted from logs, memory, or storage | Full account access until detected | +| **No Request Binding** | Token authorizes any request, not specific operations | Attacker can perform any authorized action | -**Mitigations for Bearer Token Usage**: +**Mitigations**: -| Risk | Mitigation | -|------|------------| -| Domain hijacking | Certificate pinning, domain monitoring, short token expiry | -| Token interception | mTLS, OAuth 2.0 with PKCE, token binding | -| Token replay | Short expiration (15 min), one-time-use tokens, request signing | -| Credential theft | Hardware security modules, secure enclaves | +| Risk | Mitigation | Implementation | +|------|------------|----------------| +| Token replay | Short expiration | ≀15 minute token lifetime | +| Token replay | Request signing | HMAC signature on payload + timestamp | +| Credential theft | Secure storage | HSM, secure enclaves, encrypted at rest | +| Credential theft | Anomaly detection | Alert on unusual access patterns | +| No request binding | Request signing | Signature covers specific operation parameters | + + +**Common Misconception**: OAuth 2.0 does not protect against domain hijacking. See [Domain Trust Assumption](#domain-trust-assumption) for why switching to OAuth doesn't address this threat. + -**When to Use Stronger Authentication**: +**When to Add Request Signing**: -For high-risk operations (`create_media_buy`, `update_media_buy`), consider: -- **OAuth 2.0 with PKCE**: Provides bilateral verification through the authorization flow -- **Mutual TLS (mTLS)**: Cryptographically verifies both client and server identity -- **Request Signing**: HMAC or asymmetric signatures on request payloads +For financial operations, request signing provides transaction integrity regardless of authentication mechanism: +- Proves the request wasn't modified in transit +- Binds the token to specific operation parameters +- Provides non-repudiation for audit purposes -**Example: Request Signing for Financial Operations**: +### Request Signing for Financial Operations ```typescript // Additional security layer for high-value transactions interface SignedRequest { diff --git a/v2.6-rc/docs/reference/signals-quick-reference.mdx b/dist/docs/2.5.3/reference/signals-quick-reference.mdx similarity index 100% rename from v2.6-rc/docs/reference/signals-quick-reference.mdx rename to dist/docs/2.5.3/reference/signals-quick-reference.mdx diff --git a/v2.6-rc/docs/signals/overview.mdx b/dist/docs/2.5.3/signals/overview.mdx similarity index 100% rename from v2.6-rc/docs/signals/overview.mdx rename to dist/docs/2.5.3/signals/overview.mdx diff --git a/v2.6-rc/docs/signals/specification.mdx b/dist/docs/2.5.3/signals/specification.mdx similarity index 96% rename from v2.6-rc/docs/signals/specification.mdx rename to dist/docs/2.5.3/signals/specification.mdx index 2e511a628e..7ea619d577 100644 --- a/v2.6-rc/docs/signals/specification.mdx +++ b/dist/docs/2.5.3/signals/specification.mdx @@ -35,10 +35,10 @@ The Signals Activation Protocol operates within the broader [AdCP Ecosystem Laye Every signal discovery request involves two key roles: #### Orchestrator -The platform or system making the API request to the signal platform: +The platform or system making the API request to the signal agent: - **Examples**: Scope3, Claude AI assistant, 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 signal platform +- **Account**: Has technical credentials and API access to the signal agent #### Principal The entity on whose behalf the request is being made: @@ -48,7 +48,7 @@ The entity on whose behalf the request is being made: #### How This Works in Practice -1. **Request Flow**: Orchestrator β†’ Signal Platform (on behalf of Principal) β†’ Decisioning Platform +1. **Request Flow**: Orchestrator β†’ Signal Agent (on behalf of Principal) β†’ Decisioning Platform 2. **Authentication**: Orchestrator authenticates with technical credentials 3. **Authorization**: Principal's identity determines available signals and pricing 4. **Activation**: Signals are activated for Principal's account on the decisioning platform @@ -95,7 +95,7 @@ The entity on whose behalf the request is being made: #### Private Signal Agents Agents owned by the principal with exclusive access: -- **Examples**: Walmart's internal signal platform, retailer first-party data, weather APIs, location providers +- **Examples**: Walmart's internal data systems, retailer first-party data, weather APIs, location providers - **Business Model**: No signal costs (workflow orchestration only) - **Access**: Only visible and accessible to the owning principal - **Discovery**: Not discoverable by other principals @@ -146,9 +146,9 @@ The identifier assigned by the decisioning platform after activation: - **Timing**: Only available after successful activation - **Variability**: May differ by platform and account for the same signal -### Agent vs Data Provider +### Signal Agent vs Data Provider -- **Agent**: The signal platform facilitating access (e.g., LiveRamp, Experian) +- **Signal Agent**: The MCP server facilitating signal access (e.g., LiveRamp, Experian) - **Data Provider**: The original source of the signal data (e.g., Polk, Acxiom, Peer39, weather services, location providers) A signal agent may host segments from multiple data providers in their marketplace. @@ -268,14 +268,14 @@ Some operations (like signal activation) are asynchronous and include: Each MCP session involves two levels of identification: #### Orchestrator Authentication -The technical credentials used by the orchestrator to authenticate with the signal platform: +The technical credentials used by the orchestrator to authenticate with the signal agent: - **API Keys**: Technical access credentials for the orchestrator platform - **Session Scope**: Determines what operations the orchestrator can perform -- **Platform Permissions**: What signal platforms the orchestrator can access +- **Agent Permissions**: What signal agents the orchestrator can access -#### Principal Authorization +#### Principal Authorization The principal's identity determines business-level access and pricing: -- **Account Relationship**: Whether the principal has a direct relationship with the signal platform +- **Account Relationship**: Whether the principal has a direct relationship with the signal agent - **Pricing Tier**: Negotiated rates, marketplace rates, or enterprise discounts - **Signal Access**: Private signals, premium segments, or marketplace-only access - **Billing Account**: Where usage charges are applied diff --git a/v2.6-rc/docs/signals/tasks/activate_signal.mdx b/dist/docs/2.5.3/signals/tasks/activate_signal.mdx similarity index 100% rename from v2.6-rc/docs/signals/tasks/activate_signal.mdx rename to dist/docs/2.5.3/signals/tasks/activate_signal.mdx diff --git a/v2.6-rc/docs/signals/tasks/get_signals.mdx b/dist/docs/2.5.3/signals/tasks/get_signals.mdx similarity index 100% rename from v2.6-rc/docs/signals/tasks/get_signals.mdx rename to dist/docs/2.5.3/signals/tasks/get_signals.mdx diff --git a/v2.6-rc/docs/spec-guidelines.md b/dist/docs/2.5.3/spec-guidelines.md similarity index 100% rename from v2.6-rc/docs/spec-guidelines.md rename to dist/docs/2.5.3/spec-guidelines.md diff --git a/dist/schemas/2.6.0/adagents.json b/dist/schemas/3.0.0-beta.1/adagents.json similarity index 100% rename from dist/schemas/2.6.0/adagents.json rename to dist/schemas/3.0.0-beta.1/adagents.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/build-creative-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/build-creative-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/build-creative-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/build-creative-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/build-creative-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/build-creative-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/build-creative-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/build-creative-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/create-media-buy-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/create-media-buy-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/create-media-buy-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/create-media-buy-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/create-media-buy-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/create-media-buy-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/create-media-buy-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/create-media-buy-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/get-media-buy-delivery-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/get-media-buy-delivery-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/get-media-buy-delivery-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/get-media-buy-delivery-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/get-media-buy-delivery-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/get-media-buy-delivery-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/get-media-buy-delivery-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/get-media-buy-delivery-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/get-products-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/get-products-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/get-products-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/get-products-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/get-products-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/get-products-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/get-products-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/get-products-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/list-authorized-properties-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/list-authorized-properties-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/list-authorized-properties-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/list-authorized-properties-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/list-authorized-properties-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/list-authorized-properties-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/list-authorized-properties-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/list-authorized-properties-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/list-creative-formats-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creative-formats-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/list-creative-formats-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creative-formats-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/list-creative-formats-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creative-formats-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/list-creative-formats-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creative-formats-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/list-creatives-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creatives-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/list-creatives-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creatives-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/list-creatives-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creatives-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/list-creatives-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/list-creatives-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/package-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/package-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/package-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/package-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/provide-performance-feedback-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/provide-performance-feedback-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/provide-performance-feedback-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/provide-performance-feedback-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/provide-performance-feedback-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/provide-performance-feedback-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/provide-performance-feedback-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/provide-performance-feedback-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/sync-creatives-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/sync-creatives-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/sync-creatives-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/sync-creatives-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/sync-creatives-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/sync-creatives-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/sync-creatives-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/sync-creatives-response.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/update-media-buy-request.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/update-media-buy-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/update-media-buy-request.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/update-media-buy-request.json diff --git a/dist/schemas/2.6.0/bundled/media-buy/update-media-buy-response.json b/dist/schemas/3.0.0-beta.1/bundled/media-buy/update-media-buy-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/media-buy/update-media-buy-response.json rename to dist/schemas/3.0.0-beta.1/bundled/media-buy/update-media-buy-response.json diff --git a/dist/schemas/2.6.0/bundled/signals/activate-signal-request.json b/dist/schemas/3.0.0-beta.1/bundled/signals/activate-signal-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/signals/activate-signal-request.json rename to dist/schemas/3.0.0-beta.1/bundled/signals/activate-signal-request.json diff --git a/dist/schemas/2.6.0/bundled/signals/activate-signal-response.json b/dist/schemas/3.0.0-beta.1/bundled/signals/activate-signal-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/signals/activate-signal-response.json rename to dist/schemas/3.0.0-beta.1/bundled/signals/activate-signal-response.json diff --git a/dist/schemas/2.6.0/bundled/signals/get-signals-request.json b/dist/schemas/3.0.0-beta.1/bundled/signals/get-signals-request.json similarity index 100% rename from dist/schemas/2.6.0/bundled/signals/get-signals-request.json rename to dist/schemas/3.0.0-beta.1/bundled/signals/get-signals-request.json diff --git a/dist/schemas/2.6.0/bundled/signals/get-signals-response.json b/dist/schemas/3.0.0-beta.1/bundled/signals/get-signals-response.json similarity index 100% rename from dist/schemas/2.6.0/bundled/signals/get-signals-response.json rename to dist/schemas/3.0.0-beta.1/bundled/signals/get-signals-response.json diff --git a/dist/schemas/2.6.0/core/activation-key.json b/dist/schemas/3.0.0-beta.1/core/activation-key.json similarity index 100% rename from dist/schemas/2.6.0/core/activation-key.json rename to dist/schemas/3.0.0-beta.1/core/activation-key.json diff --git a/dist/schemas/2.6.0/core/assets/audio-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/audio-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/audio-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/audio-asset.json diff --git a/dist/schemas/2.6.0/core/assets/css-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/css-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/css-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/css-asset.json diff --git a/dist/schemas/2.6.0/core/assets/daast-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/daast-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/daast-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/daast-asset.json diff --git a/dist/schemas/2.6.0/core/assets/html-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/html-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/html-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/html-asset.json diff --git a/dist/schemas/2.6.0/core/assets/image-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/image-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/image-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/image-asset.json diff --git a/dist/schemas/2.6.0/core/assets/javascript-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/javascript-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/javascript-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/javascript-asset.json diff --git a/dist/schemas/2.6.0/core/assets/markdown-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/markdown-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/markdown-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/markdown-asset.json diff --git a/dist/schemas/2.6.0/core/assets/text-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/text-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/text-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/text-asset.json diff --git a/dist/schemas/2.6.0/core/assets/url-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/url-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/url-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/url-asset.json diff --git a/dist/schemas/2.6.0/core/assets/vast-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/vast-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/vast-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/vast-asset.json diff --git a/dist/schemas/2.6.0/core/assets/video-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/video-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/video-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/video-asset.json diff --git a/dist/schemas/2.6.0/core/assets/webhook-asset.json b/dist/schemas/3.0.0-beta.1/core/assets/webhook-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/assets/webhook-asset.json rename to dist/schemas/3.0.0-beta.1/core/assets/webhook-asset.json diff --git a/dist/schemas/2.6.0/core/async-response-data.json b/dist/schemas/3.0.0-beta.1/core/async-response-data.json similarity index 100% rename from dist/schemas/2.6.0/core/async-response-data.json rename to dist/schemas/3.0.0-beta.1/core/async-response-data.json diff --git a/dist/schemas/2.6.0/core/brand-manifest-ref.json b/dist/schemas/3.0.0-beta.1/core/brand-manifest-ref.json similarity index 100% rename from dist/schemas/2.6.0/core/brand-manifest-ref.json rename to dist/schemas/3.0.0-beta.1/core/brand-manifest-ref.json diff --git a/dist/schemas/2.6.0/core/brand-manifest.json b/dist/schemas/3.0.0-beta.1/core/brand-manifest.json similarity index 100% rename from dist/schemas/2.6.0/core/brand-manifest.json rename to dist/schemas/3.0.0-beta.1/core/brand-manifest.json diff --git a/dist/schemas/2.6.0/core/context.json b/dist/schemas/3.0.0-beta.1/core/context.json similarity index 100% rename from dist/schemas/2.6.0/core/context.json rename to dist/schemas/3.0.0-beta.1/core/context.json diff --git a/dist/schemas/2.6.0/core/creative-asset.json b/dist/schemas/3.0.0-beta.1/core/creative-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/creative-asset.json rename to dist/schemas/3.0.0-beta.1/core/creative-asset.json diff --git a/dist/schemas/2.6.0/core/creative-assignment.json b/dist/schemas/3.0.0-beta.1/core/creative-assignment.json similarity index 100% rename from dist/schemas/2.6.0/core/creative-assignment.json rename to dist/schemas/3.0.0-beta.1/core/creative-assignment.json diff --git a/dist/schemas/2.6.0/core/creative-filters.json b/dist/schemas/3.0.0-beta.1/core/creative-filters.json similarity index 100% rename from dist/schemas/2.6.0/core/creative-filters.json rename to dist/schemas/3.0.0-beta.1/core/creative-filters.json diff --git a/dist/schemas/2.6.0/core/creative-manifest.json b/dist/schemas/3.0.0-beta.1/core/creative-manifest.json similarity index 100% rename from dist/schemas/2.6.0/core/creative-manifest.json rename to dist/schemas/3.0.0-beta.1/core/creative-manifest.json diff --git a/dist/schemas/2.6.0/core/creative-policy.json b/dist/schemas/3.0.0-beta.1/core/creative-policy.json similarity index 100% rename from dist/schemas/2.6.0/core/creative-policy.json rename to dist/schemas/3.0.0-beta.1/core/creative-policy.json diff --git a/dist/schemas/2.6.0/core/delivery-metrics.json b/dist/schemas/3.0.0-beta.1/core/delivery-metrics.json similarity index 100% rename from dist/schemas/2.6.0/core/delivery-metrics.json rename to dist/schemas/3.0.0-beta.1/core/delivery-metrics.json diff --git a/dist/schemas/2.6.0/core/deployment.json b/dist/schemas/3.0.0-beta.1/core/deployment.json similarity index 100% rename from dist/schemas/2.6.0/core/deployment.json rename to dist/schemas/3.0.0-beta.1/core/deployment.json diff --git a/dist/schemas/2.6.0/core/destination.json b/dist/schemas/3.0.0-beta.1/core/destination.json similarity index 100% rename from dist/schemas/2.6.0/core/destination.json rename to dist/schemas/3.0.0-beta.1/core/destination.json diff --git a/dist/schemas/2.6.0/core/error.json b/dist/schemas/3.0.0-beta.1/core/error.json similarity index 100% rename from dist/schemas/2.6.0/core/error.json rename to dist/schemas/3.0.0-beta.1/core/error.json diff --git a/dist/schemas/2.6.0/core/ext.json b/dist/schemas/3.0.0-beta.1/core/ext.json similarity index 100% rename from dist/schemas/2.6.0/core/ext.json rename to dist/schemas/3.0.0-beta.1/core/ext.json diff --git a/dist/schemas/2.6.0/core/format-id.json b/dist/schemas/3.0.0-beta.1/core/format-id.json similarity index 100% rename from dist/schemas/2.6.0/core/format-id.json rename to dist/schemas/3.0.0-beta.1/core/format-id.json diff --git a/dist/schemas/2.6.0/core/format.json b/dist/schemas/3.0.0-beta.1/core/format.json similarity index 100% rename from dist/schemas/2.6.0/core/format.json rename to dist/schemas/3.0.0-beta.1/core/format.json diff --git a/dist/schemas/2.6.0/core/frequency-cap.json b/dist/schemas/3.0.0-beta.1/core/frequency-cap.json similarity index 100% rename from dist/schemas/2.6.0/core/frequency-cap.json rename to dist/schemas/3.0.0-beta.1/core/frequency-cap.json diff --git a/dist/schemas/2.6.0/core/identifier.json b/dist/schemas/3.0.0-beta.1/core/identifier.json similarity index 100% rename from dist/schemas/2.6.0/core/identifier.json rename to dist/schemas/3.0.0-beta.1/core/identifier.json diff --git a/dist/schemas/2.6.0/core/mcp-webhook-payload.json b/dist/schemas/3.0.0-beta.1/core/mcp-webhook-payload.json similarity index 100% rename from dist/schemas/2.6.0/core/mcp-webhook-payload.json rename to dist/schemas/3.0.0-beta.1/core/mcp-webhook-payload.json diff --git a/dist/schemas/2.6.0/core/measurement.json b/dist/schemas/3.0.0-beta.1/core/measurement.json similarity index 100% rename from dist/schemas/2.6.0/core/measurement.json rename to dist/schemas/3.0.0-beta.1/core/measurement.json diff --git a/dist/schemas/2.6.0/core/media-buy.json b/dist/schemas/3.0.0-beta.1/core/media-buy.json similarity index 100% rename from dist/schemas/2.6.0/core/media-buy.json rename to dist/schemas/3.0.0-beta.1/core/media-buy.json diff --git a/dist/schemas/2.6.0/core/package.json b/dist/schemas/3.0.0-beta.1/core/package.json similarity index 100% rename from dist/schemas/2.6.0/core/package.json rename to dist/schemas/3.0.0-beta.1/core/package.json diff --git a/dist/schemas/2.6.0/core/performance-feedback.json b/dist/schemas/3.0.0-beta.1/core/performance-feedback.json similarity index 100% rename from dist/schemas/2.6.0/core/performance-feedback.json rename to dist/schemas/3.0.0-beta.1/core/performance-feedback.json diff --git a/dist/schemas/2.6.0/core/placement.json b/dist/schemas/3.0.0-beta.1/core/placement.json similarity index 100% rename from dist/schemas/2.6.0/core/placement.json rename to dist/schemas/3.0.0-beta.1/core/placement.json diff --git a/dist/schemas/2.6.0/core/pricing-option.json b/dist/schemas/3.0.0-beta.1/core/pricing-option.json similarity index 100% rename from dist/schemas/2.6.0/core/pricing-option.json rename to dist/schemas/3.0.0-beta.1/core/pricing-option.json diff --git a/dist/schemas/2.6.0/core/product-filters.json b/dist/schemas/3.0.0-beta.1/core/product-filters.json similarity index 100% rename from dist/schemas/2.6.0/core/product-filters.json rename to dist/schemas/3.0.0-beta.1/core/product-filters.json diff --git a/dist/schemas/2.6.0/core/product.json b/dist/schemas/3.0.0-beta.1/core/product.json similarity index 100% rename from dist/schemas/2.6.0/core/product.json rename to dist/schemas/3.0.0-beta.1/core/product.json diff --git a/dist/schemas/2.6.0/core/promoted-offerings.json b/dist/schemas/3.0.0-beta.1/core/promoted-offerings.json similarity index 100% rename from dist/schemas/2.6.0/core/promoted-offerings.json rename to dist/schemas/3.0.0-beta.1/core/promoted-offerings.json diff --git a/dist/schemas/2.6.0/core/promoted-products.json b/dist/schemas/3.0.0-beta.1/core/promoted-products.json similarity index 100% rename from dist/schemas/2.6.0/core/promoted-products.json rename to dist/schemas/3.0.0-beta.1/core/promoted-products.json diff --git a/dist/schemas/2.6.0/core/property-id.json b/dist/schemas/3.0.0-beta.1/core/property-id.json similarity index 100% rename from dist/schemas/2.6.0/core/property-id.json rename to dist/schemas/3.0.0-beta.1/core/property-id.json diff --git a/dist/schemas/2.6.0/core/property-list-ref.json b/dist/schemas/3.0.0-beta.1/core/property-list-ref.json similarity index 100% rename from dist/schemas/2.6.0/core/property-list-ref.json rename to dist/schemas/3.0.0-beta.1/core/property-list-ref.json diff --git a/dist/schemas/2.6.0/core/property-tag.json b/dist/schemas/3.0.0-beta.1/core/property-tag.json similarity index 100% rename from dist/schemas/2.6.0/core/property-tag.json rename to dist/schemas/3.0.0-beta.1/core/property-tag.json diff --git a/dist/schemas/2.6.0/core/property.json b/dist/schemas/3.0.0-beta.1/core/property.json similarity index 100% rename from dist/schemas/2.6.0/core/property.json rename to dist/schemas/3.0.0-beta.1/core/property.json diff --git a/dist/schemas/2.6.0/core/protocol-envelope.json b/dist/schemas/3.0.0-beta.1/core/protocol-envelope.json similarity index 100% rename from dist/schemas/2.6.0/core/protocol-envelope.json rename to dist/schemas/3.0.0-beta.1/core/protocol-envelope.json diff --git a/dist/schemas/2.6.0/core/publisher-property-selector.json b/dist/schemas/3.0.0-beta.1/core/publisher-property-selector.json similarity index 100% rename from dist/schemas/2.6.0/core/publisher-property-selector.json rename to dist/schemas/3.0.0-beta.1/core/publisher-property-selector.json diff --git a/dist/schemas/2.6.0/core/push-notification-config.json b/dist/schemas/3.0.0-beta.1/core/push-notification-config.json similarity index 100% rename from dist/schemas/2.6.0/core/push-notification-config.json rename to dist/schemas/3.0.0-beta.1/core/push-notification-config.json diff --git a/dist/schemas/2.6.0/core/reporting-capabilities.json b/dist/schemas/3.0.0-beta.1/core/reporting-capabilities.json similarity index 100% rename from dist/schemas/2.6.0/core/reporting-capabilities.json rename to dist/schemas/3.0.0-beta.1/core/reporting-capabilities.json diff --git a/dist/schemas/2.6.0/core/response.json b/dist/schemas/3.0.0-beta.1/core/response.json similarity index 100% rename from dist/schemas/2.6.0/core/response.json rename to dist/schemas/3.0.0-beta.1/core/response.json diff --git a/dist/schemas/2.6.0/core/signal-filters.json b/dist/schemas/3.0.0-beta.1/core/signal-filters.json similarity index 100% rename from dist/schemas/2.6.0/core/signal-filters.json rename to dist/schemas/3.0.0-beta.1/core/signal-filters.json diff --git a/dist/schemas/2.6.0/core/start-timing.json b/dist/schemas/3.0.0-beta.1/core/start-timing.json similarity index 100% rename from dist/schemas/2.6.0/core/start-timing.json rename to dist/schemas/3.0.0-beta.1/core/start-timing.json diff --git a/dist/schemas/2.6.0/core/sub-asset.json b/dist/schemas/3.0.0-beta.1/core/sub-asset.json similarity index 100% rename from dist/schemas/2.6.0/core/sub-asset.json rename to dist/schemas/3.0.0-beta.1/core/sub-asset.json diff --git a/dist/schemas/2.6.0/core/targeting.json b/dist/schemas/3.0.0-beta.1/core/targeting.json similarity index 100% rename from dist/schemas/2.6.0/core/targeting.json rename to dist/schemas/3.0.0-beta.1/core/targeting.json diff --git a/dist/schemas/2.6.0/core/tasks-get-request.json b/dist/schemas/3.0.0-beta.1/core/tasks-get-request.json similarity index 100% rename from dist/schemas/2.6.0/core/tasks-get-request.json rename to dist/schemas/3.0.0-beta.1/core/tasks-get-request.json diff --git a/dist/schemas/2.6.0/core/tasks-get-response.json b/dist/schemas/3.0.0-beta.1/core/tasks-get-response.json similarity index 100% rename from dist/schemas/2.6.0/core/tasks-get-response.json rename to dist/schemas/3.0.0-beta.1/core/tasks-get-response.json diff --git a/dist/schemas/2.6.0/core/tasks-list-request.json b/dist/schemas/3.0.0-beta.1/core/tasks-list-request.json similarity index 100% rename from dist/schemas/2.6.0/core/tasks-list-request.json rename to dist/schemas/3.0.0-beta.1/core/tasks-list-request.json diff --git a/dist/schemas/2.6.0/core/tasks-list-response.json b/dist/schemas/3.0.0-beta.1/core/tasks-list-response.json similarity index 100% rename from dist/schemas/2.6.0/core/tasks-list-response.json rename to dist/schemas/3.0.0-beta.1/core/tasks-list-response.json diff --git a/dist/schemas/2.6.0/creative/asset-types/index.json b/dist/schemas/3.0.0-beta.1/creative/asset-types/index.json similarity index 100% rename from dist/schemas/2.6.0/creative/asset-types/index.json rename to dist/schemas/3.0.0-beta.1/creative/asset-types/index.json diff --git a/dist/schemas/2.6.0/creative/list-creative-formats-request.json b/dist/schemas/3.0.0-beta.1/creative/list-creative-formats-request.json similarity index 100% rename from dist/schemas/2.6.0/creative/list-creative-formats-request.json rename to dist/schemas/3.0.0-beta.1/creative/list-creative-formats-request.json diff --git a/dist/schemas/2.6.0/creative/list-creative-formats-response.json b/dist/schemas/3.0.0-beta.1/creative/list-creative-formats-response.json similarity index 100% rename from dist/schemas/2.6.0/creative/list-creative-formats-response.json rename to dist/schemas/3.0.0-beta.1/creative/list-creative-formats-response.json diff --git a/dist/schemas/2.6.0/creative/preview-creative-request.json b/dist/schemas/3.0.0-beta.1/creative/preview-creative-request.json similarity index 100% rename from dist/schemas/2.6.0/creative/preview-creative-request.json rename to dist/schemas/3.0.0-beta.1/creative/preview-creative-request.json diff --git a/dist/schemas/2.6.0/creative/preview-creative-response.json b/dist/schemas/3.0.0-beta.1/creative/preview-creative-response.json similarity index 100% rename from dist/schemas/2.6.0/creative/preview-creative-response.json rename to dist/schemas/3.0.0-beta.1/creative/preview-creative-response.json diff --git a/dist/schemas/2.6.0/creative/preview-render.json b/dist/schemas/3.0.0-beta.1/creative/preview-render.json similarity index 100% rename from dist/schemas/2.6.0/creative/preview-render.json rename to dist/schemas/3.0.0-beta.1/creative/preview-render.json diff --git a/dist/schemas/2.6.0/enums/adcp-domain.json b/dist/schemas/3.0.0-beta.1/enums/adcp-domain.json similarity index 100% rename from dist/schemas/2.6.0/enums/adcp-domain.json rename to dist/schemas/3.0.0-beta.1/enums/adcp-domain.json diff --git a/dist/schemas/2.6.0/enums/asset-content-type.json b/dist/schemas/3.0.0-beta.1/enums/asset-content-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/asset-content-type.json rename to dist/schemas/3.0.0-beta.1/enums/asset-content-type.json diff --git a/dist/schemas/2.6.0/enums/auth-scheme.json b/dist/schemas/3.0.0-beta.1/enums/auth-scheme.json similarity index 100% rename from dist/schemas/2.6.0/enums/auth-scheme.json rename to dist/schemas/3.0.0-beta.1/enums/auth-scheme.json diff --git a/dist/schemas/2.6.0/enums/available-metric.json b/dist/schemas/3.0.0-beta.1/enums/available-metric.json similarity index 100% rename from dist/schemas/2.6.0/enums/available-metric.json rename to dist/schemas/3.0.0-beta.1/enums/available-metric.json diff --git a/dist/schemas/2.6.0/enums/channels.json b/dist/schemas/3.0.0-beta.1/enums/channels.json similarity index 100% rename from dist/schemas/2.6.0/enums/channels.json rename to dist/schemas/3.0.0-beta.1/enums/channels.json diff --git a/dist/schemas/2.6.0/enums/co-branding-requirement.json b/dist/schemas/3.0.0-beta.1/enums/co-branding-requirement.json similarity index 100% rename from dist/schemas/2.6.0/enums/co-branding-requirement.json rename to dist/schemas/3.0.0-beta.1/enums/co-branding-requirement.json diff --git a/dist/schemas/2.6.0/enums/creative-action.json b/dist/schemas/3.0.0-beta.1/enums/creative-action.json similarity index 100% rename from dist/schemas/2.6.0/enums/creative-action.json rename to dist/schemas/3.0.0-beta.1/enums/creative-action.json diff --git a/dist/schemas/2.6.0/enums/creative-agent-capability.json b/dist/schemas/3.0.0-beta.1/enums/creative-agent-capability.json similarity index 100% rename from dist/schemas/2.6.0/enums/creative-agent-capability.json rename to dist/schemas/3.0.0-beta.1/enums/creative-agent-capability.json diff --git a/dist/schemas/2.6.0/enums/creative-sort-field.json b/dist/schemas/3.0.0-beta.1/enums/creative-sort-field.json similarity index 100% rename from dist/schemas/2.6.0/enums/creative-sort-field.json rename to dist/schemas/3.0.0-beta.1/enums/creative-sort-field.json diff --git a/dist/schemas/2.6.0/enums/creative-status.json b/dist/schemas/3.0.0-beta.1/enums/creative-status.json similarity index 100% rename from dist/schemas/2.6.0/enums/creative-status.json rename to dist/schemas/3.0.0-beta.1/enums/creative-status.json diff --git a/dist/schemas/2.6.0/enums/daast-tracking-event.json b/dist/schemas/3.0.0-beta.1/enums/daast-tracking-event.json similarity index 100% rename from dist/schemas/2.6.0/enums/daast-tracking-event.json rename to dist/schemas/3.0.0-beta.1/enums/daast-tracking-event.json diff --git a/dist/schemas/2.6.0/enums/daast-version.json b/dist/schemas/3.0.0-beta.1/enums/daast-version.json similarity index 100% rename from dist/schemas/2.6.0/enums/daast-version.json rename to dist/schemas/3.0.0-beta.1/enums/daast-version.json diff --git a/dist/schemas/2.6.0/enums/delivery-type.json b/dist/schemas/3.0.0-beta.1/enums/delivery-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/delivery-type.json rename to dist/schemas/3.0.0-beta.1/enums/delivery-type.json diff --git a/dist/schemas/2.6.0/enums/dimension-unit.json b/dist/schemas/3.0.0-beta.1/enums/dimension-unit.json similarity index 100% rename from dist/schemas/2.6.0/enums/dimension-unit.json rename to dist/schemas/3.0.0-beta.1/enums/dimension-unit.json diff --git a/dist/schemas/2.6.0/enums/feed-format.json b/dist/schemas/3.0.0-beta.1/enums/feed-format.json similarity index 100% rename from dist/schemas/2.6.0/enums/feed-format.json rename to dist/schemas/3.0.0-beta.1/enums/feed-format.json diff --git a/dist/schemas/2.6.0/enums/feedback-source.json b/dist/schemas/3.0.0-beta.1/enums/feedback-source.json similarity index 100% rename from dist/schemas/2.6.0/enums/feedback-source.json rename to dist/schemas/3.0.0-beta.1/enums/feedback-source.json diff --git a/dist/schemas/2.6.0/enums/format-category.json b/dist/schemas/3.0.0-beta.1/enums/format-category.json similarity index 100% rename from dist/schemas/2.6.0/enums/format-category.json rename to dist/schemas/3.0.0-beta.1/enums/format-category.json diff --git a/dist/schemas/2.6.0/enums/format-id-parameter.json b/dist/schemas/3.0.0-beta.1/enums/format-id-parameter.json similarity index 100% rename from dist/schemas/2.6.0/enums/format-id-parameter.json rename to dist/schemas/3.0.0-beta.1/enums/format-id-parameter.json diff --git a/dist/schemas/2.6.0/enums/frequency-cap-scope.json b/dist/schemas/3.0.0-beta.1/enums/frequency-cap-scope.json similarity index 100% rename from dist/schemas/2.6.0/enums/frequency-cap-scope.json rename to dist/schemas/3.0.0-beta.1/enums/frequency-cap-scope.json diff --git a/dist/schemas/2.6.0/enums/history-entry-type.json b/dist/schemas/3.0.0-beta.1/enums/history-entry-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/history-entry-type.json rename to dist/schemas/3.0.0-beta.1/enums/history-entry-type.json diff --git a/dist/schemas/2.6.0/enums/http-method.json b/dist/schemas/3.0.0-beta.1/enums/http-method.json similarity index 100% rename from dist/schemas/2.6.0/enums/http-method.json rename to dist/schemas/3.0.0-beta.1/enums/http-method.json diff --git a/dist/schemas/2.6.0/enums/identifier-types.json b/dist/schemas/3.0.0-beta.1/enums/identifier-types.json similarity index 100% rename from dist/schemas/2.6.0/enums/identifier-types.json rename to dist/schemas/3.0.0-beta.1/enums/identifier-types.json diff --git a/dist/schemas/2.6.0/enums/javascript-module-type.json b/dist/schemas/3.0.0-beta.1/enums/javascript-module-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/javascript-module-type.json rename to dist/schemas/3.0.0-beta.1/enums/javascript-module-type.json diff --git a/dist/schemas/2.6.0/enums/landing-page-requirement.json b/dist/schemas/3.0.0-beta.1/enums/landing-page-requirement.json similarity index 100% rename from dist/schemas/2.6.0/enums/landing-page-requirement.json rename to dist/schemas/3.0.0-beta.1/enums/landing-page-requirement.json diff --git a/dist/schemas/2.6.0/enums/markdown-flavor.json b/dist/schemas/3.0.0-beta.1/enums/markdown-flavor.json similarity index 100% rename from dist/schemas/2.6.0/enums/markdown-flavor.json rename to dist/schemas/3.0.0-beta.1/enums/markdown-flavor.json diff --git a/dist/schemas/2.6.0/enums/media-buy-status.json b/dist/schemas/3.0.0-beta.1/enums/media-buy-status.json similarity index 100% rename from dist/schemas/2.6.0/enums/media-buy-status.json rename to dist/schemas/3.0.0-beta.1/enums/media-buy-status.json diff --git a/dist/schemas/2.6.0/enums/metric-type.json b/dist/schemas/3.0.0-beta.1/enums/metric-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/metric-type.json rename to dist/schemas/3.0.0-beta.1/enums/metric-type.json diff --git a/dist/schemas/2.6.0/enums/notification-type.json b/dist/schemas/3.0.0-beta.1/enums/notification-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/notification-type.json rename to dist/schemas/3.0.0-beta.1/enums/notification-type.json diff --git a/dist/schemas/2.6.0/enums/pacing.json b/dist/schemas/3.0.0-beta.1/enums/pacing.json similarity index 100% rename from dist/schemas/2.6.0/enums/pacing.json rename to dist/schemas/3.0.0-beta.1/enums/pacing.json diff --git a/dist/schemas/2.6.0/enums/preview-output-format.json b/dist/schemas/3.0.0-beta.1/enums/preview-output-format.json similarity index 100% rename from dist/schemas/2.6.0/enums/preview-output-format.json rename to dist/schemas/3.0.0-beta.1/enums/preview-output-format.json diff --git a/dist/schemas/2.6.0/enums/pricing-model.json b/dist/schemas/3.0.0-beta.1/enums/pricing-model.json similarity index 100% rename from dist/schemas/2.6.0/enums/pricing-model.json rename to dist/schemas/3.0.0-beta.1/enums/pricing-model.json diff --git a/dist/schemas/2.6.0/enums/property-type.json b/dist/schemas/3.0.0-beta.1/enums/property-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/property-type.json rename to dist/schemas/3.0.0-beta.1/enums/property-type.json diff --git a/dist/schemas/2.6.0/enums/publisher-identifier-types.json b/dist/schemas/3.0.0-beta.1/enums/publisher-identifier-types.json similarity index 100% rename from dist/schemas/2.6.0/enums/publisher-identifier-types.json rename to dist/schemas/3.0.0-beta.1/enums/publisher-identifier-types.json diff --git a/dist/schemas/2.6.0/enums/reporting-frequency.json b/dist/schemas/3.0.0-beta.1/enums/reporting-frequency.json similarity index 100% rename from dist/schemas/2.6.0/enums/reporting-frequency.json rename to dist/schemas/3.0.0-beta.1/enums/reporting-frequency.json diff --git a/dist/schemas/2.6.0/enums/signal-catalog-type.json b/dist/schemas/3.0.0-beta.1/enums/signal-catalog-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/signal-catalog-type.json rename to dist/schemas/3.0.0-beta.1/enums/signal-catalog-type.json diff --git a/dist/schemas/2.6.0/enums/sort-direction.json b/dist/schemas/3.0.0-beta.1/enums/sort-direction.json similarity index 100% rename from dist/schemas/2.6.0/enums/sort-direction.json rename to dist/schemas/3.0.0-beta.1/enums/sort-direction.json diff --git a/dist/schemas/2.6.0/enums/standard-format-ids.json b/dist/schemas/3.0.0-beta.1/enums/standard-format-ids.json similarity index 100% rename from dist/schemas/2.6.0/enums/standard-format-ids.json rename to dist/schemas/3.0.0-beta.1/enums/standard-format-ids.json diff --git a/dist/schemas/2.6.0/enums/task-status.json b/dist/schemas/3.0.0-beta.1/enums/task-status.json similarity index 100% rename from dist/schemas/2.6.0/enums/task-status.json rename to dist/schemas/3.0.0-beta.1/enums/task-status.json diff --git a/dist/schemas/2.6.0/enums/task-type.json b/dist/schemas/3.0.0-beta.1/enums/task-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/task-type.json rename to dist/schemas/3.0.0-beta.1/enums/task-type.json diff --git a/dist/schemas/2.6.0/enums/update-frequency.json b/dist/schemas/3.0.0-beta.1/enums/update-frequency.json similarity index 100% rename from dist/schemas/2.6.0/enums/update-frequency.json rename to dist/schemas/3.0.0-beta.1/enums/update-frequency.json diff --git a/dist/schemas/2.6.0/enums/url-asset-type.json b/dist/schemas/3.0.0-beta.1/enums/url-asset-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/url-asset-type.json rename to dist/schemas/3.0.0-beta.1/enums/url-asset-type.json diff --git a/dist/schemas/2.6.0/enums/validation-mode.json b/dist/schemas/3.0.0-beta.1/enums/validation-mode.json similarity index 100% rename from dist/schemas/2.6.0/enums/validation-mode.json rename to dist/schemas/3.0.0-beta.1/enums/validation-mode.json diff --git a/dist/schemas/2.6.0/enums/vast-tracking-event.json b/dist/schemas/3.0.0-beta.1/enums/vast-tracking-event.json similarity index 100% rename from dist/schemas/2.6.0/enums/vast-tracking-event.json rename to dist/schemas/3.0.0-beta.1/enums/vast-tracking-event.json diff --git a/dist/schemas/2.6.0/enums/vast-version.json b/dist/schemas/3.0.0-beta.1/enums/vast-version.json similarity index 100% rename from dist/schemas/2.6.0/enums/vast-version.json rename to dist/schemas/3.0.0-beta.1/enums/vast-version.json diff --git a/dist/schemas/2.6.0/enums/webhook-response-type.json b/dist/schemas/3.0.0-beta.1/enums/webhook-response-type.json similarity index 100% rename from dist/schemas/2.6.0/enums/webhook-response-type.json rename to dist/schemas/3.0.0-beta.1/enums/webhook-response-type.json diff --git a/dist/schemas/2.6.0/enums/webhook-security-method.json b/dist/schemas/3.0.0-beta.1/enums/webhook-security-method.json similarity index 100% rename from dist/schemas/2.6.0/enums/webhook-security-method.json rename to dist/schemas/3.0.0-beta.1/enums/webhook-security-method.json diff --git a/dist/schemas/2.6.0/extensions/extension-meta.json b/dist/schemas/3.0.0-beta.1/extensions/extension-meta.json similarity index 100% rename from dist/schemas/2.6.0/extensions/extension-meta.json rename to dist/schemas/3.0.0-beta.1/extensions/extension-meta.json diff --git a/dist/schemas/2.6.0/extensions/index.json b/dist/schemas/3.0.0-beta.1/extensions/index.json similarity index 100% rename from dist/schemas/2.6.0/extensions/index.json rename to dist/schemas/3.0.0-beta.1/extensions/index.json diff --git a/dist/schemas/2.6.0/index.json b/dist/schemas/3.0.0-beta.1/index.json similarity index 100% rename from dist/schemas/2.6.0/index.json rename to dist/schemas/3.0.0-beta.1/index.json diff --git a/dist/schemas/2.6.0/media-buy/build-creative-request.json b/dist/schemas/3.0.0-beta.1/media-buy/build-creative-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/build-creative-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/build-creative-request.json diff --git a/dist/schemas/2.6.0/media-buy/build-creative-response.json b/dist/schemas/3.0.0-beta.1/media-buy/build-creative-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/build-creative-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/build-creative-response.json diff --git a/dist/schemas/2.6.0/media-buy/create-media-buy-async-response-input-required.json b/dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-async-response-input-required.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/create-media-buy-async-response-input-required.json rename to dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-async-response-input-required.json diff --git a/dist/schemas/2.6.0/media-buy/create-media-buy-async-response-submitted.json b/dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-async-response-submitted.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/create-media-buy-async-response-submitted.json rename to dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-async-response-submitted.json diff --git a/dist/schemas/2.6.0/media-buy/create-media-buy-async-response-working.json b/dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-async-response-working.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/create-media-buy-async-response-working.json rename to dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-async-response-working.json diff --git a/dist/schemas/2.6.0/media-buy/create-media-buy-request.json b/dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/create-media-buy-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-request.json diff --git a/dist/schemas/2.6.0/media-buy/create-media-buy-response.json b/dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/create-media-buy-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/create-media-buy-response.json diff --git a/dist/schemas/2.6.0/media-buy/get-media-buy-delivery-request.json b/dist/schemas/3.0.0-beta.1/media-buy/get-media-buy-delivery-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/get-media-buy-delivery-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/get-media-buy-delivery-request.json diff --git a/dist/schemas/2.6.0/media-buy/get-media-buy-delivery-response.json b/dist/schemas/3.0.0-beta.1/media-buy/get-media-buy-delivery-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/get-media-buy-delivery-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/get-media-buy-delivery-response.json diff --git a/dist/schemas/2.6.0/media-buy/get-products-async-response-input-required.json b/dist/schemas/3.0.0-beta.1/media-buy/get-products-async-response-input-required.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/get-products-async-response-input-required.json rename to dist/schemas/3.0.0-beta.1/media-buy/get-products-async-response-input-required.json diff --git a/dist/schemas/2.6.0/media-buy/get-products-async-response-submitted.json b/dist/schemas/3.0.0-beta.1/media-buy/get-products-async-response-submitted.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/get-products-async-response-submitted.json rename to dist/schemas/3.0.0-beta.1/media-buy/get-products-async-response-submitted.json diff --git a/dist/schemas/2.6.0/media-buy/get-products-async-response-working.json b/dist/schemas/3.0.0-beta.1/media-buy/get-products-async-response-working.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/get-products-async-response-working.json rename to dist/schemas/3.0.0-beta.1/media-buy/get-products-async-response-working.json diff --git a/dist/schemas/2.6.0/media-buy/get-products-request.json b/dist/schemas/3.0.0-beta.1/media-buy/get-products-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/get-products-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/get-products-request.json diff --git a/dist/schemas/2.6.0/media-buy/get-products-response.json b/dist/schemas/3.0.0-beta.1/media-buy/get-products-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/get-products-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/get-products-response.json diff --git a/dist/schemas/2.6.0/media-buy/list-authorized-properties-request.json b/dist/schemas/3.0.0-beta.1/media-buy/list-authorized-properties-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/list-authorized-properties-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/list-authorized-properties-request.json diff --git a/dist/schemas/2.6.0/media-buy/list-authorized-properties-response.json b/dist/schemas/3.0.0-beta.1/media-buy/list-authorized-properties-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/list-authorized-properties-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/list-authorized-properties-response.json diff --git a/dist/schemas/2.6.0/media-buy/list-creative-formats-request.json b/dist/schemas/3.0.0-beta.1/media-buy/list-creative-formats-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/list-creative-formats-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/list-creative-formats-request.json diff --git a/dist/schemas/2.6.0/media-buy/list-creative-formats-response.json b/dist/schemas/3.0.0-beta.1/media-buy/list-creative-formats-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/list-creative-formats-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/list-creative-formats-response.json diff --git a/dist/schemas/2.6.0/media-buy/list-creatives-request.json b/dist/schemas/3.0.0-beta.1/media-buy/list-creatives-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/list-creatives-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/list-creatives-request.json diff --git a/dist/schemas/2.6.0/media-buy/list-creatives-response.json b/dist/schemas/3.0.0-beta.1/media-buy/list-creatives-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/list-creatives-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/list-creatives-response.json diff --git a/dist/schemas/2.6.0/media-buy/package-request.json b/dist/schemas/3.0.0-beta.1/media-buy/package-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/package-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/package-request.json diff --git a/dist/schemas/2.6.0/media-buy/provide-performance-feedback-request.json b/dist/schemas/3.0.0-beta.1/media-buy/provide-performance-feedback-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/provide-performance-feedback-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/provide-performance-feedback-request.json diff --git a/dist/schemas/2.6.0/media-buy/provide-performance-feedback-response.json b/dist/schemas/3.0.0-beta.1/media-buy/provide-performance-feedback-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/provide-performance-feedback-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/provide-performance-feedback-response.json diff --git a/dist/schemas/2.6.0/media-buy/sync-creatives-async-response-input-required.json b/dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-async-response-input-required.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/sync-creatives-async-response-input-required.json rename to dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-async-response-input-required.json diff --git a/dist/schemas/2.6.0/media-buy/sync-creatives-async-response-submitted.json b/dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-async-response-submitted.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/sync-creatives-async-response-submitted.json rename to dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-async-response-submitted.json diff --git a/dist/schemas/2.6.0/media-buy/sync-creatives-async-response-working.json b/dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-async-response-working.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/sync-creatives-async-response-working.json rename to dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-async-response-working.json diff --git a/dist/schemas/2.6.0/media-buy/sync-creatives-request.json b/dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/sync-creatives-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-request.json diff --git a/dist/schemas/2.6.0/media-buy/sync-creatives-response.json b/dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/sync-creatives-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/sync-creatives-response.json diff --git a/dist/schemas/2.6.0/media-buy/update-media-buy-async-response-input-required.json b/dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-async-response-input-required.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/update-media-buy-async-response-input-required.json rename to dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-async-response-input-required.json diff --git a/dist/schemas/2.6.0/media-buy/update-media-buy-async-response-submitted.json b/dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-async-response-submitted.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/update-media-buy-async-response-submitted.json rename to dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-async-response-submitted.json diff --git a/dist/schemas/2.6.0/media-buy/update-media-buy-async-response-working.json b/dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-async-response-working.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/update-media-buy-async-response-working.json rename to dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-async-response-working.json diff --git a/dist/schemas/2.6.0/media-buy/update-media-buy-request.json b/dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-request.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/update-media-buy-request.json rename to dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-request.json diff --git a/dist/schemas/2.6.0/media-buy/update-media-buy-response.json b/dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-response.json similarity index 100% rename from dist/schemas/2.6.0/media-buy/update-media-buy-response.json rename to dist/schemas/3.0.0-beta.1/media-buy/update-media-buy-response.json diff --git a/dist/schemas/2.6.0/pricing-options/cpc-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/cpc-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/cpc-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/cpc-option.json diff --git a/dist/schemas/2.6.0/pricing-options/cpcv-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/cpcv-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/cpcv-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/cpcv-option.json diff --git a/dist/schemas/2.6.0/pricing-options/cpm-auction-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/cpm-auction-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/cpm-auction-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/cpm-auction-option.json diff --git a/dist/schemas/2.6.0/pricing-options/cpm-fixed-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/cpm-fixed-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/cpm-fixed-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/cpm-fixed-option.json diff --git a/dist/schemas/2.6.0/pricing-options/cpp-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/cpp-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/cpp-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/cpp-option.json diff --git a/dist/schemas/2.6.0/pricing-options/cpv-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/cpv-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/cpv-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/cpv-option.json diff --git a/dist/schemas/2.6.0/pricing-options/flat-rate-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/flat-rate-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/flat-rate-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/flat-rate-option.json diff --git a/dist/schemas/2.6.0/pricing-options/vcpm-auction-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/vcpm-auction-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/vcpm-auction-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/vcpm-auction-option.json diff --git a/dist/schemas/2.6.0/pricing-options/vcpm-fixed-option.json b/dist/schemas/3.0.0-beta.1/pricing-options/vcpm-fixed-option.json similarity index 100% rename from dist/schemas/2.6.0/pricing-options/vcpm-fixed-option.json rename to dist/schemas/3.0.0-beta.1/pricing-options/vcpm-fixed-option.json diff --git a/dist/schemas/2.6.0/property/authorization-result.json b/dist/schemas/3.0.0-beta.1/property/authorization-result.json similarity index 100% rename from dist/schemas/2.6.0/property/authorization-result.json rename to dist/schemas/3.0.0-beta.1/property/authorization-result.json diff --git a/dist/schemas/2.6.0/property/base-property-source.json b/dist/schemas/3.0.0-beta.1/property/base-property-source.json similarity index 100% rename from dist/schemas/2.6.0/property/base-property-source.json rename to dist/schemas/3.0.0-beta.1/property/base-property-source.json diff --git a/dist/schemas/2.6.0/property/create-property-list-request.json b/dist/schemas/3.0.0-beta.1/property/create-property-list-request.json similarity index 100% rename from dist/schemas/2.6.0/property/create-property-list-request.json rename to dist/schemas/3.0.0-beta.1/property/create-property-list-request.json diff --git a/dist/schemas/2.6.0/property/create-property-list-response.json b/dist/schemas/3.0.0-beta.1/property/create-property-list-response.json similarity index 100% rename from dist/schemas/2.6.0/property/create-property-list-response.json rename to dist/schemas/3.0.0-beta.1/property/create-property-list-response.json diff --git a/dist/schemas/2.6.0/property/delete-property-list-request.json b/dist/schemas/3.0.0-beta.1/property/delete-property-list-request.json similarity index 100% rename from dist/schemas/2.6.0/property/delete-property-list-request.json rename to dist/schemas/3.0.0-beta.1/property/delete-property-list-request.json diff --git a/dist/schemas/2.6.0/property/delete-property-list-response.json b/dist/schemas/3.0.0-beta.1/property/delete-property-list-response.json similarity index 100% rename from dist/schemas/2.6.0/property/delete-property-list-response.json rename to dist/schemas/3.0.0-beta.1/property/delete-property-list-response.json diff --git a/dist/schemas/2.6.0/property/delivery-record.json b/dist/schemas/3.0.0-beta.1/property/delivery-record.json similarity index 100% rename from dist/schemas/2.6.0/property/delivery-record.json rename to dist/schemas/3.0.0-beta.1/property/delivery-record.json diff --git a/dist/schemas/2.6.0/property/feature-requirement.json b/dist/schemas/3.0.0-beta.1/property/feature-requirement.json similarity index 100% rename from dist/schemas/2.6.0/property/feature-requirement.json rename to dist/schemas/3.0.0-beta.1/property/feature-requirement.json diff --git a/dist/schemas/2.6.0/property/get-property-features-request.json b/dist/schemas/3.0.0-beta.1/property/get-property-features-request.json similarity index 100% rename from dist/schemas/2.6.0/property/get-property-features-request.json rename to dist/schemas/3.0.0-beta.1/property/get-property-features-request.json diff --git a/dist/schemas/2.6.0/property/get-property-features-response.json b/dist/schemas/3.0.0-beta.1/property/get-property-features-response.json similarity index 100% rename from dist/schemas/2.6.0/property/get-property-features-response.json rename to dist/schemas/3.0.0-beta.1/property/get-property-features-response.json diff --git a/dist/schemas/2.6.0/property/get-property-list-request.json b/dist/schemas/3.0.0-beta.1/property/get-property-list-request.json similarity index 100% rename from dist/schemas/2.6.0/property/get-property-list-request.json rename to dist/schemas/3.0.0-beta.1/property/get-property-list-request.json diff --git a/dist/schemas/2.6.0/property/get-property-list-response.json b/dist/schemas/3.0.0-beta.1/property/get-property-list-response.json similarity index 100% rename from dist/schemas/2.6.0/property/get-property-list-response.json rename to dist/schemas/3.0.0-beta.1/property/get-property-list-response.json diff --git a/dist/schemas/2.6.0/property/list-property-features-request.json b/dist/schemas/3.0.0-beta.1/property/list-property-features-request.json similarity index 100% rename from dist/schemas/2.6.0/property/list-property-features-request.json rename to dist/schemas/3.0.0-beta.1/property/list-property-features-request.json diff --git a/dist/schemas/2.6.0/property/list-property-features-response.json b/dist/schemas/3.0.0-beta.1/property/list-property-features-response.json similarity index 100% rename from dist/schemas/2.6.0/property/list-property-features-response.json rename to dist/schemas/3.0.0-beta.1/property/list-property-features-response.json diff --git a/dist/schemas/2.6.0/property/list-property-lists-request.json b/dist/schemas/3.0.0-beta.1/property/list-property-lists-request.json similarity index 100% rename from dist/schemas/2.6.0/property/list-property-lists-request.json rename to dist/schemas/3.0.0-beta.1/property/list-property-lists-request.json diff --git a/dist/schemas/2.6.0/property/list-property-lists-response.json b/dist/schemas/3.0.0-beta.1/property/list-property-lists-response.json similarity index 100% rename from dist/schemas/2.6.0/property/list-property-lists-response.json rename to dist/schemas/3.0.0-beta.1/property/list-property-lists-response.json diff --git a/dist/schemas/2.6.0/property/property-error.json b/dist/schemas/3.0.0-beta.1/property/property-error.json similarity index 100% rename from dist/schemas/2.6.0/property/property-error.json rename to dist/schemas/3.0.0-beta.1/property/property-error.json diff --git a/dist/schemas/2.6.0/property/property-feature-definition.json b/dist/schemas/3.0.0-beta.1/property/property-feature-definition.json similarity index 100% rename from dist/schemas/2.6.0/property/property-feature-definition.json rename to dist/schemas/3.0.0-beta.1/property/property-feature-definition.json diff --git a/dist/schemas/2.6.0/property/property-feature-result.json b/dist/schemas/3.0.0-beta.1/property/property-feature-result.json similarity index 100% rename from dist/schemas/2.6.0/property/property-feature-result.json rename to dist/schemas/3.0.0-beta.1/property/property-feature-result.json diff --git a/dist/schemas/2.6.0/property/property-feature-value.json b/dist/schemas/3.0.0-beta.1/property/property-feature-value.json similarity index 100% rename from dist/schemas/2.6.0/property/property-feature-value.json rename to dist/schemas/3.0.0-beta.1/property/property-feature-value.json diff --git a/dist/schemas/2.6.0/property/property-feature.json b/dist/schemas/3.0.0-beta.1/property/property-feature.json similarity index 100% rename from dist/schemas/2.6.0/property/property-feature.json rename to dist/schemas/3.0.0-beta.1/property/property-feature.json diff --git a/dist/schemas/2.6.0/property/property-list-changed-webhook.json b/dist/schemas/3.0.0-beta.1/property/property-list-changed-webhook.json similarity index 100% rename from dist/schemas/2.6.0/property/property-list-changed-webhook.json rename to dist/schemas/3.0.0-beta.1/property/property-list-changed-webhook.json diff --git a/dist/schemas/2.6.0/property/property-list-filters.json b/dist/schemas/3.0.0-beta.1/property/property-list-filters.json similarity index 100% rename from dist/schemas/2.6.0/property/property-list-filters.json rename to dist/schemas/3.0.0-beta.1/property/property-list-filters.json diff --git a/dist/schemas/2.6.0/property/property-list.json b/dist/schemas/3.0.0-beta.1/property/property-list.json similarity index 100% rename from dist/schemas/2.6.0/property/property-list.json rename to dist/schemas/3.0.0-beta.1/property/property-list.json diff --git a/dist/schemas/2.6.0/property/update-property-list-request.json b/dist/schemas/3.0.0-beta.1/property/update-property-list-request.json similarity index 100% rename from dist/schemas/2.6.0/property/update-property-list-request.json rename to dist/schemas/3.0.0-beta.1/property/update-property-list-request.json diff --git a/dist/schemas/2.6.0/property/update-property-list-response.json b/dist/schemas/3.0.0-beta.1/property/update-property-list-response.json similarity index 100% rename from dist/schemas/2.6.0/property/update-property-list-response.json rename to dist/schemas/3.0.0-beta.1/property/update-property-list-response.json diff --git a/dist/schemas/2.6.0/property/validate-property-delivery-request.json b/dist/schemas/3.0.0-beta.1/property/validate-property-delivery-request.json similarity index 100% rename from dist/schemas/2.6.0/property/validate-property-delivery-request.json rename to dist/schemas/3.0.0-beta.1/property/validate-property-delivery-request.json diff --git a/dist/schemas/2.6.0/property/validate-property-delivery-response.json b/dist/schemas/3.0.0-beta.1/property/validate-property-delivery-response.json similarity index 100% rename from dist/schemas/2.6.0/property/validate-property-delivery-response.json rename to dist/schemas/3.0.0-beta.1/property/validate-property-delivery-response.json diff --git a/dist/schemas/2.6.0/property/validation-result.json b/dist/schemas/3.0.0-beta.1/property/validation-result.json similarity index 100% rename from dist/schemas/2.6.0/property/validation-result.json rename to dist/schemas/3.0.0-beta.1/property/validation-result.json diff --git a/dist/schemas/2.6.0/protocols/adcp-extension.json b/dist/schemas/3.0.0-beta.1/protocols/adcp-extension.json similarity index 100% rename from dist/schemas/2.6.0/protocols/adcp-extension.json rename to dist/schemas/3.0.0-beta.1/protocols/adcp-extension.json diff --git a/dist/schemas/2.6.0/signals/activate-signal-request.json b/dist/schemas/3.0.0-beta.1/signals/activate-signal-request.json similarity index 100% rename from dist/schemas/2.6.0/signals/activate-signal-request.json rename to dist/schemas/3.0.0-beta.1/signals/activate-signal-request.json diff --git a/dist/schemas/2.6.0/signals/activate-signal-response.json b/dist/schemas/3.0.0-beta.1/signals/activate-signal-response.json similarity index 100% rename from dist/schemas/2.6.0/signals/activate-signal-response.json rename to dist/schemas/3.0.0-beta.1/signals/activate-signal-response.json diff --git a/dist/schemas/2.6.0/signals/get-signals-request.json b/dist/schemas/3.0.0-beta.1/signals/get-signals-request.json similarity index 100% rename from dist/schemas/2.6.0/signals/get-signals-request.json rename to dist/schemas/3.0.0-beta.1/signals/get-signals-request.json diff --git a/dist/schemas/2.6.0/signals/get-signals-response.json b/dist/schemas/3.0.0-beta.1/signals/get-signals-response.json similarity index 100% rename from dist/schemas/2.6.0/signals/get-signals-response.json rename to dist/schemas/3.0.0-beta.1/signals/get-signals-response.json diff --git a/docs.json b/docs.json index 14a8ef09e6..2e1d590e79 100644 --- a/docs.json +++ b/docs.json @@ -52,7 +52,7 @@ "navigation": { "versions": [ { - "version": "2.5", + "version": "3.0-beta", "groups": [ { "group": "Getting Started", @@ -204,9 +204,7 @@ { "group": "Reference", "pages": [ - "docs/reference/media-buy-quick-reference", - "docs/reference/signals-quick-reference", - "docs/reference/creative-quick-reference", + "docs/reference/media-channel-taxonomy", "docs/reference/roadmap", "docs/reference/release-notes", "docs/reference/changelog", @@ -228,51 +226,51 @@ ] }, { - "version": "2.6-rc", + "version": "2.5", "groups": [ { "group": "Getting Started", "pages": [ - "v2.6-rc/docs/intro", - "v2.6-rc/docs/quickstart" + "dist/docs/2.5.3/intro", + "dist/docs/2.5.3/quickstart" ] }, { "group": "Protocols", "pages": [ - "v2.6-rc/docs/protocols/getting-started", - "v2.6-rc/docs/protocols/core-concepts", + "dist/docs/2.5.3/protocols/getting-started", + "dist/docs/2.5.3/protocols/core-concepts", { "group": "Choose Your Protocol", "pages": [ - "v2.6-rc/docs/protocols/mcp-guide", - "v2.6-rc/docs/protocols/a2a-guide", - "v2.6-rc/docs/protocols/a2a-response-format" + "dist/docs/2.5.3/protocols/mcp-guide", + "dist/docs/2.5.3/protocols/a2a-guide", + "dist/docs/2.5.3/protocols/a2a-response-format" ] }, - "v2.6-rc/docs/protocols/protocol-comparison", - "v2.6-rc/docs/protocols/context-management", - "v2.6-rc/docs/protocols/task-management", - "v2.6-rc/docs/protocols/envelope-examples" + "dist/docs/2.5.3/protocols/protocol-comparison", + "dist/docs/2.5.3/protocols/context-management", + "dist/docs/2.5.3/protocols/task-management", + "dist/docs/2.5.3/protocols/envelope-examples" ] }, { "group": "adagents.json", "pages": [ - "v2.6-rc/docs/media-buy/capability-discovery/adagents", - "v2.6-rc/docs/media-buy/capability-discovery/authorized-properties" + "dist/docs/2.5.3/media-buy/capability-discovery/adagents", + "dist/docs/2.5.3/media-buy/capability-discovery/authorized-properties" ] }, { "group": "Signals Protocol", "pages": [ - "v2.6-rc/docs/signals/overview", - "v2.6-rc/docs/signals/specification", + "dist/docs/2.5.3/signals/overview", + "dist/docs/2.5.3/signals/specification", { "group": "Tasks", "pages": [ - "v2.6-rc/docs/signals/tasks/get_signals", - "v2.6-rc/docs/signals/tasks/activate_signal" + "dist/docs/2.5.3/signals/tasks/get_signals", + "dist/docs/2.5.3/signals/tasks/activate_signal" ] } ] @@ -280,68 +278,68 @@ { "group": "Curation Protocol", "pages": [ - "v2.6-rc/docs/curation/coming-soon" + "dist/docs/2.5.3/curation/coming-soon" ] }, { "group": "Media Buy Protocol", "pages": [ - "v2.6-rc/docs/media-buy/index", + "dist/docs/2.5.3/media-buy/index", { "group": "Task Reference", "pages": [ - "v2.6-rc/docs/media-buy/task-reference/index", - "v2.6-rc/docs/media-buy/task-reference/get_products", - "v2.6-rc/docs/media-buy/task-reference/list_creative_formats", - "v2.6-rc/docs/media-buy/task-reference/list_authorized_properties", - "v2.6-rc/docs/media-buy/task-reference/create_media_buy", - "v2.6-rc/docs/media-buy/task-reference/list_creatives", - "v2.6-rc/docs/media-buy/task-reference/sync_creatives", - "v2.6-rc/docs/media-buy/task-reference/get_media_buy_delivery", - "v2.6-rc/docs/media-buy/task-reference/update_media_buy", - "v2.6-rc/docs/media-buy/task-reference/provide_performance_feedback" + "dist/docs/2.5.3/media-buy/task-reference/index", + "dist/docs/2.5.3/media-buy/task-reference/get_products", + "dist/docs/2.5.3/media-buy/task-reference/list_creative_formats", + "dist/docs/2.5.3/media-buy/task-reference/list_authorized_properties", + "dist/docs/2.5.3/media-buy/task-reference/create_media_buy", + "dist/docs/2.5.3/media-buy/task-reference/list_creatives", + "dist/docs/2.5.3/media-buy/task-reference/sync_creatives", + "dist/docs/2.5.3/media-buy/task-reference/get_media_buy_delivery", + "dist/docs/2.5.3/media-buy/task-reference/update_media_buy", + "dist/docs/2.5.3/media-buy/task-reference/provide_performance_feedback" ] }, { "group": "Capability Discovery", "pages": [ - "v2.6-rc/docs/media-buy/capability-discovery/index", - "v2.6-rc/docs/media-buy/capability-discovery/implementing-standard-formats" + "dist/docs/2.5.3/media-buy/capability-discovery/index", + "dist/docs/2.5.3/media-buy/capability-discovery/implementing-standard-formats" ] }, { "group": "Product Discovery", "pages": [ - "v2.6-rc/docs/media-buy/product-discovery/index", - "v2.6-rc/docs/media-buy/product-discovery/brief-expectations", - "v2.6-rc/docs/media-buy/product-discovery/example-briefs", - "v2.6-rc/docs/media-buy/product-discovery/media-products" + "dist/docs/2.5.3/media-buy/product-discovery/index", + "dist/docs/2.5.3/media-buy/product-discovery/brief-expectations", + "dist/docs/2.5.3/media-buy/product-discovery/example-briefs", + "dist/docs/2.5.3/media-buy/product-discovery/media-products" ] }, { "group": "Media Buys", "pages": [ - "v2.6-rc/docs/media-buy/media-buys/index", - "v2.6-rc/docs/media-buy/media-buys/optimization-reporting", - "v2.6-rc/docs/media-buy/media-buys/policy-compliance" + "dist/docs/2.5.3/media-buy/media-buys/index", + "dist/docs/2.5.3/media-buy/media-buys/optimization-reporting", + "dist/docs/2.5.3/media-buy/media-buys/policy-compliance" ] }, { "group": "Creatives", "pages": [ - "v2.6-rc/docs/media-buy/creatives/index" + "dist/docs/2.5.3/media-buy/creatives/index" ] }, { "group": "Advanced Topics", "pages": [ - "v2.6-rc/docs/media-buy/advanced-topics/index", - "v2.6-rc/docs/media-buy/advanced-topics/pricing-models", - "v2.6-rc/docs/media-buy/advanced-topics/targeting", - "v2.6-rc/docs/media-buy/advanced-topics/agentic-execution-engine", - "v2.6-rc/docs/media-buy/advanced-topics/principals-and-security", - "v2.6-rc/docs/media-buy/advanced-topics/testing", - "v2.6-rc/docs/media-buy/advanced-topics/orchestrator-design" + "dist/docs/2.5.3/media-buy/advanced-topics/index", + "dist/docs/2.5.3/media-buy/advanced-topics/pricing-models", + "dist/docs/2.5.3/media-buy/advanced-topics/targeting", + "dist/docs/2.5.3/media-buy/advanced-topics/agentic-execution-engine", + "dist/docs/2.5.3/media-buy/advanced-topics/principals-and-security", + "dist/docs/2.5.3/media-buy/advanced-topics/testing", + "dist/docs/2.5.3/media-buy/advanced-topics/orchestrator-design" ] } ] @@ -349,30 +347,30 @@ { "group": "Creative", "pages": [ - "v2.6-rc/docs/creative/index", - "v2.6-rc/docs/creative/formats", - "v2.6-rc/docs/creative/asset-types", - "v2.6-rc/docs/creative/creative-manifests", - "v2.6-rc/docs/creative/brand-manifest", - "v2.6-rc/docs/creative/universal-macros", - "v2.6-rc/docs/creative/implementing-creative-agents", - "v2.6-rc/docs/creative/generative-creative", + "dist/docs/2.5.3/creative/index", + "dist/docs/2.5.3/creative/formats", + "dist/docs/2.5.3/creative/asset-types", + "dist/docs/2.5.3/creative/creative-manifests", + "dist/docs/2.5.3/creative/brand-manifest", + "dist/docs/2.5.3/creative/universal-macros", + "dist/docs/2.5.3/creative/implementing-creative-agents", + "dist/docs/2.5.3/creative/generative-creative", { "group": "Channel Guides", "pages": [ - "v2.6-rc/docs/creative/channels/video", - "v2.6-rc/docs/creative/channels/display", - "v2.6-rc/docs/creative/channels/audio", - "v2.6-rc/docs/creative/channels/dooh", - "v2.6-rc/docs/creative/channels/carousels" + "dist/docs/2.5.3/creative/channels/video", + "dist/docs/2.5.3/creative/channels/display", + "dist/docs/2.5.3/creative/channels/audio", + "dist/docs/2.5.3/creative/channels/dooh", + "dist/docs/2.5.3/creative/channels/carousels" ] }, { "group": "Task Reference", "pages": [ - "v2.6-rc/docs/creative/task-reference/build_creative", - "v2.6-rc/docs/creative/task-reference/preview_creative", - "v2.6-rc/docs/creative/task-reference/list_creative_formats" + "dist/docs/2.5.3/creative/task-reference/build_creative", + "dist/docs/2.5.3/creative/task-reference/preview_creative", + "dist/docs/2.5.3/creative/task-reference/list_creative_formats" ] } ] @@ -380,22 +378,25 @@ { "group": "Reference", "pages": [ - "v2.6-rc/docs/reference/roadmap", - "v2.6-rc/docs/reference/release-notes", - "v2.6-rc/docs/reference/changelog", - "v2.6-rc/docs/reference/schema-versioning", - "v2.6-rc/docs/reference/extensions-and-context", - "v2.6-rc/docs/reference/error-codes", - "v2.6-rc/docs/reference/data-models", - "v2.6-rc/docs/reference/authentication", - "v2.6-rc/docs/reference/security", - "v2.6-rc/docs/reference/glossary" + "dist/docs/2.5.3/reference/media-buy-quick-reference", + "dist/docs/2.5.3/reference/signals-quick-reference", + "dist/docs/2.5.3/reference/creative-quick-reference", + "dist/docs/2.5.3/reference/roadmap", + "dist/docs/2.5.3/reference/release-notes", + "dist/docs/2.5.3/reference/changelog", + "dist/docs/2.5.3/reference/schema-versioning", + "dist/docs/2.5.3/reference/extensions-and-context", + "dist/docs/2.5.3/reference/error-codes", + "dist/docs/2.5.3/reference/data-models", + "dist/docs/2.5.3/reference/authentication", + "dist/docs/2.5.3/reference/security", + "dist/docs/2.5.3/reference/glossary" ] }, { "group": "Community", "pages": [ - "v2.6-rc/docs/community/working-group" + "dist/docs/2.5.3/community/working-group" ] } ] diff --git a/docs/creative/asset-types.mdx b/docs/creative/asset-types.mdx index c3d58aa685..927fbedf1f 100644 --- a/docs/creative/asset-types.mdx +++ b/docs/creative/asset-types.mdx @@ -338,7 +338,9 @@ The keys in the assets object correspond to the `asset_id` values defined in the ## Usage in Creative Formats -Creative formats specify their required assets using `assets_required` (an array): +Creative formats specify their assets using the `assets` array. Each asset has a `required` boolean: +- **`required: true`** - Asset MUST be provided for a valid creative +- **`required: false`** - Asset is optional, enhances the creative (e.g., companion banners, third-party tracking pixels) ```json { @@ -346,7 +348,7 @@ Creative formats specify their required assets using `assets_required` (an array "agent_url": "https://creative.adcontextprotocol.org", "id": "video_15s_hosted" }, - "assets_required": [ + "assets": [ { "item_type": "individual", "asset_id": "video_file", @@ -359,11 +361,41 @@ Creative formats specify their required assets using `assets_required` (an array "acceptable_resolutions": ["1920x1080", "1280x720"], "max_file_size_mb": 30 } + }, + { + "item_type": "individual", + "asset_id": "impression_tracker", + "asset_type": "url", + "required": false, + "requirements": { + "format": ["url"], + "description": "Third-party impression tracking pixel URL" + } + } + ], + + // DEPRECATED: Use "assets" above instead. Kept for backward compatibility. + "assets_required": [ + { + "item_type": "individual", + "asset_id": "video_file", + "asset_type": "video", + "requirements": { + "duration_seconds": 15, + "acceptable_formats": ["mp4"], + "acceptable_codecs": ["h264"], + "acceptable_resolutions": ["1920x1080", "1280x720"], + "max_file_size_mb": 30 + } } ] } ``` + +**Backward Compatibility**: The `assets_required` field is deprecated but still supported. Existing implementations can continue using `assets_required` for required assets only. New implementations should use `assets` with the `required` boolean for full asset discovery. + + ## Repeatable Asset Groups For formats with asset sequences (like carousels, slideshows, stories), see the [Carousel & Multi-Asset Formats guide](/docs/creative/channels/carousels) for complete documentation on repeatable asset group patterns. diff --git a/docs/creative/brand-manifest.mdx b/docs/creative/brand-manifest.mdx index 692a6fe993..cb80d8f985 100644 --- a/docs/creative/brand-manifest.mdx +++ b/docs/creative/brand-manifest.mdx @@ -22,6 +22,7 @@ Brand manifests solve a key problem: how to efficiently identify advertisers and ### Key Benefits - **Know Your Customer**: Publishers can verify advertisers meet their standards +- **Privacy Transparency**: Link to privacy policy for consumer consent flows - **Minimal Friction**: Start with just a name or URL, expand as needed - **Cacheable**: Same brand manifest reused across all requests - **Standardized**: Consistent format across all AdCP implementations @@ -243,6 +244,7 @@ The structure of the brand manifest object itself (whether provided inline or ho | Field | Type | Description | |-------|------|-------------| | `name` | string | Brand or business name | +| `privacy_policy_url` | string (uri) | URL to the brand's privacy policy for consumer consent flows | | `logos` | Logo[] | Brand logo assets with semantic tags | | `colors` | Colors | Brand color palette (hex format) | | `fonts` | Fonts | Brand typography guidelines | @@ -325,10 +327,21 @@ The structure of the brand manifest object itself (whether provided inline or ho ```typescript { feed_url: string; // URL to product catalog feed - feed_format?: string; // Format of the product feed (default: "google_merchant_center") + feed_format?: string; // Format: "google_merchant_center" | "facebook_catalog" | "openai_product_feed" | "custom" categories?: string[]; // Product categories available in the catalog last_updated?: string; // When the product catalog was last updated (ISO 8601) update_frequency?: string; // How frequently the catalog is updated + agentic_checkout?: AgenticCheckout; // Optional checkout endpoint configuration +} +``` + +### AgenticCheckout Object + +```typescript +{ + endpoint: string; // Base URL for checkout session API + spec: string; // Checkout API specification (e.g., "openai_agentic_checkout_v1") + supported_payment_providers?: string[]; // Payment providers (e.g., ["stripe", "adyen"]) } ``` @@ -459,7 +472,7 @@ Large retailers should provide product feeds: } ``` -**Supported Feed Formats**: RSS, JSON Feed, Product CSV +**Supported Feed Formats**: Google Merchant Center, Facebook Catalog, [OpenAI Product Feed](https://developers.openai.com/commerce/specs/feed), Custom ### 5. Asset Libraries for Enterprise @@ -492,6 +505,85 @@ Enterprise brands with large asset libraries should provide explicit assets: } ``` +## Privacy Integration + +Brand manifests support privacy transparency through the `privacy_policy_url` field. This enables AI platforms to present explicit privacy choices to users before sharing personal data with advertisers. + +### Consumer Consent Flow + +When an AI assistant helps a user engage with an advertiser (booking a flight, making a purchase, etc.), the platform can use the brand manifest's privacy policy URL to: + +1. **Present explicit consent**: "May I share your details with Delta? [View their privacy policy]" +2. **Enable informed decisions**: Users can review data practices before data handoff +3. **Support machine-readable terms**: Works alongside [MyTerms/IEEE P7012](https://myterms.info) for automated privacy negotiation + +### Example with Privacy Policy + +```json +{ + "$schema": "https://adcontextprotocol.org/schemas/v2/core/brand-manifest.json", + "name": "Delta Airlines", + "url": "https://delta.com", + "privacy_policy_url": "https://delta.com/privacy" +} +``` + +### MyTerms Discovery + +For advertisers implementing [IEEE P7012 (MyTerms)](https://myterms.info), AI platforms can discover machine-readable privacy terms from the advertiser's domain (e.g., `/.well-known/myterms`). The brand manifest's `privacy_policy_url` serves as the human-readable fallback and explicit consent path. + +## Agentic Commerce Integration + +Brand manifests support integration with AI commerce platforms through the `product_catalog` field. This enables AI agents to discover products and complete purchases on behalf of users. + +### OpenAI Commerce + +For merchants implementing [OpenAI's Commerce specifications](https://developers.openai.com/commerce), the brand manifest provides a bridge: + +```json +{ + "$schema": "https://adcontextprotocol.org/schemas/v2/core/brand-manifest.json", + "name": "Shop Example", + "url": "https://shopexample.com", + "privacy_policy_url": "https://shopexample.com/privacy", + "product_catalog": { + "feed_url": "https://shopexample.com/products.jsonl.gz", + "feed_format": "openai_product_feed", + "update_frequency": "daily", + "agentic_checkout": { + "endpoint": "https://api.shopexample.com/checkout_sessions", + "spec": "openai_agentic_checkout_v1", + "supported_payment_providers": ["stripe", "adyen"] + } + } +} +``` + +**Key fields for OpenAI Commerce:** + +| Field | Description | +|-------|-------------| +| `feed_format: "openai_product_feed"` | Indicates the feed conforms to [OpenAI's Product Feed spec](https://developers.openai.com/commerce/specs/feed) | +| `agentic_checkout.endpoint` | Base URL for [OpenAI's Agentic Checkout API](https://developers.openai.com/commerce/specs/checkout) | +| `agentic_checkout.spec` | Version identifier for the checkout spec | + +### Feed Format Mapping + +If you have an existing Google Merchant Center feed, here's how key fields map to OpenAI's spec: + +| OpenAI Field | Google Merchant Center | Notes | +|--------------|----------------------|-------| +| `item_id` | `id` | Direct mapping | +| `title` | `title` | Direct mapping | +| `description` | `description` | Direct mapping | +| `url` | `link` | Direct mapping | +| `brand` | `brand` | Direct mapping | +| `price` | `price` | OpenAI uses number + currency code | +| `availability` | `availability` | Same enum values | +| `image_url` | `image_link` | Direct mapping | +| `is_eligible_search` | N/A | OpenAI-specific flag | +| `is_eligible_checkout` | N/A | OpenAI-specific flag | + ## Evolution and Versioning Brand cards are versioned using the `metadata.version` field: diff --git a/docs/creative/formats.mdx b/docs/creative/formats.mdx index 54ae1f1c23..ba6b100d4f 100644 --- a/docs/creative/formats.mdx +++ b/docs/creative/formats.mdx @@ -222,7 +222,7 @@ Formats are JSON objects with the following key fields: }, "name": "30-Second Hosted Video", "type": "video", - "assets_required": [ + "assets": [ { "item_type": "individual", "asset_id": "video_file", @@ -234,6 +234,53 @@ Formats are JSON objects with the following key fields: "format": ["MP4"], "resolution": ["1920x1080", "1280x720"] } + }, + { + "item_type": "individual", + "asset_id": "end_card_image", + "asset_type": "image", + "asset_role": "end_card", + "required": false, + "requirements": { + "dimensions": "1920x1080", + "format": ["PNG", "JPG"] + } + }, + { + "item_type": "individual", + "asset_id": "companion_banner", + "asset_type": "image", + "asset_role": "companion", + "required": false, + "requirements": { + "dimensions": "300x250", + "format": ["PNG", "JPG", "GIF"] + } + }, + { + "item_type": "individual", + "asset_id": "impression_tracker", + "asset_type": "url", + "asset_role": "third_party_tracking", + "required": false, + "requirements": { + "description": "Third-party impression tracking pixel URL" + } + } + ], + + // DEPRECATED: Use "assets" above instead. Kept for backward compatibility. + "assets_required": [ + { + "item_type": "individual", + "asset_id": "video_file", + "asset_type": "video", + "asset_role": "hero_video", + "requirements": { + "duration": "30s", + "format": ["MP4"], + "resolution": ["1920x1080", "1280x720"] + } } ] } @@ -243,10 +290,24 @@ Formats are JSON objects with the following key fields: - **format_id**: Unique identifier (may be namespaced with `domain:id`) - **agent_url**: The creative agent authoritative for this format - **type**: Category (video, display, audio, native, dooh, rich_media) -- **assets_required**: Array of asset specifications +- **assets**: Array of all asset specifications with `required` boolean indicating mandatory vs optional +- **assets_required**: *(Deprecated)* Array of required asset specifications - use `assets` instead - **asset_role**: Identifies asset purpose (hero_image, logo, cta_button, etc.) - **renders**: Array of rendered outputs with dimensions - see below +### Asset Discovery + +The `assets` array enables comprehensive asset discovery. Each asset has a `required` boolean: + +- **`required: true`** - Asset MUST be provided for a valid creative +- **`required: false`** - Asset is optional, enhances the creative when provided (e.g., companion banners, third-party tracking pixels) + +This unified approach helps creative tools and AI agents understand the full capabilities of a format, enabling richer creative experiences when optional assets are available while clearly indicating minimum requirements. + + +**Backward Compatibility**: The `assets_required` field is deprecated but still supported. Existing implementations can continue using `assets_required` for required assets only. New implementations should use `assets` with the `required` boolean on each asset for full asset discovery. + + ### Rendered Outputs and Dimensions Formats specify their rendered outputs via the `renders` array. Most formats produce a single render, but some (companion ads, adaptive formats, multi-placement) produce multiple renders: diff --git a/docs/creative/implementing-creative-agents.mdx b/docs/creative/implementing-creative-agents.mdx index 98671f1eb2..abb86bb5d0 100644 --- a/docs/creative/implementing-creative-agents.mdx +++ b/docs/creative/implementing-creative-agents.mdx @@ -100,7 +100,7 @@ Creative agents must implement these two tasks: Return all formats your agent defines. This is how buyers discover what creative formats you support. **Key responsibilities:** -- Return complete format definitions with all `assets_required` +- Return complete format definitions with all `assets` (both required and optional) - Include your `agent_url` in each format - Use proper namespacing for `format_id` values @@ -169,7 +169,7 @@ When validating manifests: When updating format definitions: -- **Additive changes** (new optional assets) are safe +- **Additive changes** (new optional assets with `required: false` in `assets`) are safe - **Breaking changes** (removing assets, changing requirements) require new format_id - Consider versioning: `youragency.com:format_name_v2` - Maintain backward compatibility when possible diff --git a/docs/creative/task-reference/list_creative_formats.mdx b/docs/creative/task-reference/list_creative_formats.mdx index 648c6f6792..af0348f718 100644 --- a/docs/creative/task-reference/list_creative_formats.mdx +++ b/docs/creative/task-reference/list_creative_formats.mdx @@ -56,7 +56,7 @@ Formats may produce multiple rendered pieces (e.g., video + companion banner). D | Field | Description | |-------|-------------| -| `formats` | Array of full format definitions (format_id, name, type, requirements, assets_required, renders) | +| `formats` | Array of full format definitions (format_id, name, type, requirements, assets, assets_required, renders) | | `creative_agents` | Optional array of other creative agents providing additional formats | See [Format schema](https://adcontextprotocol.org/schemas/v2/core/format.json) for complete format object structure. @@ -421,7 +421,8 @@ Each format includes: | `name` | Human-readable format name | | `type` | Format type (audio, video, display, dooh) | | `requirements` | Technical requirements (duration, file types, bitrate, etc.) | -| `assets_required` | Array of required assets with specifications | +| `assets` | Array of all assets with `required` boolean indicating mandatory vs optional | +| `assets_required` | *(Deprecated)* Array of required assets - use `assets` instead | | `renders` | Array of rendered output pieces (dimensions, role) | ### Asset Roles diff --git a/v2.6-rc/docs/governance/brand-standards/index.mdx b/docs/governance/brand-standards/index.mdx similarity index 95% rename from v2.6-rc/docs/governance/brand-standards/index.mdx rename to docs/governance/brand-standards/index.mdx index 15a738adc0..16799d37fc 100644 --- a/v2.6-rc/docs/governance/brand-standards/index.mdx +++ b/docs/governance/brand-standards/index.mdx @@ -87,4 +87,4 @@ Detailed specification and task definitions for Brand Standards governance are u - **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. +See the [Governance Protocol overview](../index) for the broader context of how Brand Standards fits into AdCP governance. diff --git a/v2.6-rc/docs/governance/content-standards/artifacts.mdx b/docs/governance/content-standards/artifacts.mdx similarity index 97% rename from v2.6-rc/docs/governance/content-standards/artifacts.mdx rename to docs/governance/content-standards/artifacts.mdx index 7d2f834a2a..c38a636db0 100644 --- a/v2.6-rc/docs/governance/content-standards/artifacts.mdx +++ b/docs/governance/content-standards/artifacts.mdx @@ -300,5 +300,5 @@ The verification agent doesn't need to understand the scheme - it's opaque. The ## Related -- [Content Standards Overview](/docs/governance/content-standards) - How artifacts fit into the content standards workflow -- [calibrate_content](/docs/governance/content-standards/tasks/calibrate_content) - Sending artifacts for calibration +- [Content Standards Overview](.) - How artifacts fit into the content standards workflow +- [calibrate_content](./tasks/calibrate_content) - Sending artifacts for calibration diff --git a/v2.6-rc/docs/governance/content-standards/implementation-guide.mdx b/docs/governance/content-standards/implementation-guide.mdx similarity index 100% rename from v2.6-rc/docs/governance/content-standards/implementation-guide.mdx rename to docs/governance/content-standards/implementation-guide.mdx diff --git a/v2.6-rc/docs/governance/content-standards/index.mdx b/docs/governance/content-standards/index.mdx similarity index 89% rename from v2.6-rc/docs/governance/content-standards/index.mdx rename to docs/governance/content-standards/index.mdx index eda9db83a5..32929e96c1 100644 --- a/v2.6-rc/docs/governance/content-standards/index.mdx +++ b/docs/governance/content-standards/index.mdx @@ -43,7 +43,7 @@ This inverts the traditional model. Instead of "send us your content, we'll eval Content standards evaluation involves four key questions that buyers and sellers negotiate: -1. **What content?** - What [artifacts](/docs/governance/content-standards/artifacts) to evaluate (the ad-adjacent content) +1. **What content?** - What [artifacts](./artifacts) to evaluate (the ad-adjacent content) 2. **How much adjacency?** - How many artifacts around the ad slot to consider 3. **What sampling rate?** - What percentage of traffic to evaluate 4. **How to calibrate?** - How to align on policy interpretation before runtime @@ -204,7 +204,7 @@ Content Standards uses **natural language prompts** rather than rigid keyword li The policy prompt enables AI-powered verification agents to understand context and nuance. **Calibration** examples provide a training/test set that helps the agent interpret the policy correctly. -See [Artifacts](/docs/governance/content-standards/artifacts) for details on artifact structure and secured asset access. +See [Artifacts](./artifacts) for details on artifact structure and secured asset access. ## Scoped Standards @@ -215,10 +215,19 @@ Buyers typically maintain multiple standards configurations for different contex "standards_id": "uk_tv_zero_calorie", "name": "UK TV - zero-calorie brands", "countries_all": ["GB"], - "channels_any": ["ctv", "linear_tv"] + "channels_any": ["ctv", "linear_tv"], + "languages_any": ["en"] } ``` + +**Code Format Conventions** + +Country and language codes are **case-insensitive** - implementations must normalize before comparison. Recommended formats follow ISO standards: +- **Countries**: Uppercase ISO 3166-1 alpha-2 (e.g., `GB`, `US`, `DE`) +- **Languages**: Lowercase ISO 639-1 or BCP 47 (e.g., `en`, `de`, `fr`) + + **The buyer selects the appropriate `standards_id` when creating a media buy.** The seller receives a reference to the resolved standards - they don't need to do scope matching themselves. ## Calibration @@ -255,7 +264,7 @@ Before running campaigns, sellers calibrate their local models against the verif } ``` -See [calibrate_content](/docs/governance/content-standards/tasks/calibrate_content) for the full task specification. +See [calibrate_content](./tasks/calibrate_content) for the full task specification. ## Tasks @@ -263,24 +272,24 @@ See [calibrate_content](/docs/governance/content-standards/tasks/calibrate_conte | Task | Description | |------|-------------| -| [list_content_standards](/docs/governance/content-standards/tasks/list_content_standards) | List available standards configurations | -| [get_content_standards](/docs/governance/content-standards/tasks/get_content_standards) | Retrieve a specific standards configuration | +| [list_content_standards](./tasks/list_content_standards) | List available standards configurations | +| [get_content_standards](./tasks/get_content_standards) | Retrieve a specific standards configuration | ### Management | Task | Description | |------|-------------| -| [create_content_standards](/docs/governance/content-standards/tasks/create_content_standards) | Create a new standards configuration | -| [update_content_standards](/docs/governance/content-standards/tasks/update_content_standards) | Update an existing standards configuration | -| [delete_content_standards](/docs/governance/content-standards/tasks/delete_content_standards) | Delete a standards configuration | +| [create_content_standards](./tasks/create_content_standards) | Create a new standards configuration | +| [update_content_standards](./tasks/update_content_standards) | Update an existing standards configuration | +| [delete_content_standards](./tasks/delete_content_standards) | Delete a standards configuration | ### Calibration & Validation | Task | Description | |------|-------------| -| [calibrate_content](/docs/governance/content-standards/tasks/calibrate_content) | Collaborative dialogue to align on policy interpretation | -| [get_media_buy_artifacts](/docs/governance/content-standards/tasks/get_media_buy_artifacts) | Retrieve content artifacts from a media buy | -| [validate_content_delivery](/docs/governance/content-standards/tasks/validate_content_delivery) | Batch validation of content artifacts | +| [calibrate_content](./tasks/calibrate_content) | Collaborative dialogue to align on policy interpretation | +| [get_media_buy_artifacts](./tasks/get_media_buy_artifacts) | Retrieve content artifacts from a media buy | +| [validate_content_delivery](./tasks/validate_content_delivery) | Batch validation of content artifacts | ## Typical Providers @@ -354,5 +363,5 @@ This architecture aligns with the [IAB Tech Lab ARTF (Agentic RTB Framework)](ht ## Related -- [Artifacts](/docs/governance/content-standards/artifacts) - What artifacts are and how to structure them -- [Brand Manifest](/docs/creative/brand-manifest) - Static brand identity that can link to standards agents +- [Artifacts](./artifacts) - What artifacts are and how to structure them +- [Brand Manifest](../../creative/brand-manifest) - Static brand identity that can link to standards agents diff --git a/v2.6-rc/docs/governance/content-standards/tasks/calibrate_content.mdx b/docs/governance/content-standards/tasks/calibrate_content.mdx similarity index 96% rename from v2.6-rc/docs/governance/content-standards/tasks/calibrate_content.mdx rename to docs/governance/content-standards/tasks/calibrate_content.mdx index 801ee3bb53..e30a889ef0 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/calibrate_content.mdx +++ b/docs/governance/content-standards/tasks/calibrate_content.mdx @@ -224,5 +224,5 @@ The key insight is that the dialogue happens at the **protocol layer**, not the ## Related Tasks -- [get_content_standards](/docs/governance/content-standards/tasks/get_content_standards) - Retrieve the policies being calibrated against -- [validate_content_delivery](/docs/governance/content-standards/tasks/validate_content_delivery) - Post-campaign delivery validation +- [get_content_standards](./get_content_standards) - Retrieve the policies being calibrated against +- [validate_content_delivery](./validate_content_delivery) - Post-campaign delivery validation diff --git a/v2.6-rc/docs/governance/content-standards/tasks/create_content_standards.mdx b/docs/governance/content-standards/tasks/create_content_standards.mdx similarity index 81% rename from v2.6-rc/docs/governance/content-standards/tasks/create_content_standards.mdx rename to docs/governance/content-standards/tasks/create_content_standards.mdx index 51859ca853..f3f2b729b4 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/create_content_standards.mdx +++ b/docs/governance/content-standards/tasks/create_content_standards.mdx @@ -19,9 +19,11 @@ Create a new content standards configuration. | `policy` | string | Yes | Natural language policy prompt | | `calibration_exemplars` | object | No | Training set of pass/fail artifacts for calibration | -:::note[Brand Safety Floor Requirement] + +**Brand Safety Floor Requirement** + Implementors MUST apply a brand safety floor regardless of what policy is defined in content standards. Content that violates the floor (hate speech, illegal content, etc.) must be excluded even when no content standards are specified. AdCP does not define the floor specification; this is left to implementors and industry standards (e.g., GARM categories). -::: + ### Example Request @@ -79,12 +81,12 @@ Implementors MUST apply a brand safety floor regardless of what policy is define Multiple standards cannot have overlapping scopes for the same country/channel/language combination. When creating standards that would conflict: -1. **Check existing standards** - Use [list_content_standards](/docs/governance/content-standards/tasks/list_content_standards) filtered by your scope -2. **Update rather than create** - If standards already exist, use [update_content_standards](/docs/governance/content-standards/tasks/update_content_standards) +1. **Check existing standards** - Use [list_content_standards](./list_content_standards) filtered by your scope +2. **Update rather than create** - If standards already exist, use [update_content_standards](./update_content_standards) 3. **Narrow the scope** - Adjust countries or channels to avoid overlap ## Related Tasks -- [list_content_standards](/docs/governance/content-standards/tasks/list_content_standards) - List all configurations -- [update_content_standards](/docs/governance/content-standards/tasks/update_content_standards) - Update a configuration -- [delete_content_standards](/docs/governance/content-standards/tasks/delete_content_standards) - Delete a configuration +- [list_content_standards](./list_content_standards) - List all configurations +- [update_content_standards](./update_content_standards) - Update a configuration +- [delete_content_standards](./delete_content_standards) - Delete a configuration diff --git a/v2.6-rc/docs/governance/content-standards/tasks/delete_content_standards.mdx b/docs/governance/content-standards/tasks/delete_content_standards.mdx similarity index 69% rename from v2.6-rc/docs/governance/content-standards/tasks/delete_content_standards.mdx rename to docs/governance/content-standards/tasks/delete_content_standards.mdx index 2f38244c51..4f0482b964 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/delete_content_standards.mdx +++ b/docs/governance/content-standards/tasks/delete_content_standards.mdx @@ -62,9 +62,9 @@ Delete a content standards configuration. } ``` -Standards cannot be deleted while they are referenced by active media buys. Use [list_content_standards](/docs/governance/content-standards/tasks/list_content_standards) to identify usage, or archive standards by setting an expiration date rather than deleting. +Standards cannot be deleted while they are referenced by active media buys. Use [list_content_standards](./list_content_standards) to identify usage, or archive standards by setting an expiration date rather than deleting. ## Related Tasks -- [list_content_standards](/docs/governance/content-standards/tasks/list_content_standards) - List all configurations -- [create_content_standards](/docs/governance/content-standards/tasks/create_content_standards) - Create a new configuration +- [list_content_standards](./list_content_standards) - List all configurations +- [create_content_standards](./create_content_standards) - Create a new configuration diff --git a/v2.6-rc/docs/governance/content-standards/tasks/get_content_standards.mdx b/docs/governance/content-standards/tasks/get_content_standards.mdx similarity index 82% rename from v2.6-rc/docs/governance/content-standards/tasks/get_content_standards.mdx rename to docs/governance/content-standards/tasks/get_content_standards.mdx index dec5de452e..409dd34798 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/get_content_standards.mdx +++ b/docs/governance/content-standards/tasks/get_content_standards.mdx @@ -48,15 +48,17 @@ Retrieve content safety policies for a specific standards configuration. |-------|-------------| | `standards_id` | Unique identifier for this standards configuration | | `name` | Human-readable name | -| `countries_all` | ISO country codes - standards apply in ALL listed countries | +| `countries_all` | ISO 3166-1 alpha-2 country codes (case-insensitive, uppercase recommended) - standards apply in ALL listed countries | | `channels_any` | Ad channels - standards apply to ANY of the listed channels | -| `languages_any` | BCP 47 language tags - standards apply to content in ANY of these languages | +| `languages_any` | ISO 639-1 or BCP 47 language tags (case-insensitive, lowercase recommended) - standards apply to content in ANY of these languages | | `policy` | Natural language policy describing acceptable and unacceptable content contexts | | `calibration_exemplars` | Training/test set of content contexts (pass/fail) to calibrate policy interpretation | -:::note[Brand Safety Floor Requirement] + +**Brand Safety Floor Requirement** + Implementors MUST apply a brand safety floor regardless of what policy is defined. AdCP does not define the floor specification. -::: + ### Error Response @@ -73,5 +75,5 @@ Implementors MUST apply a brand safety floor regardless of what policy is define ## Related Tasks -- [calibrate_content](/docs/governance/content-standards/tasks/calibrate_content) - Collaborative calibration against these standards -- [list_content_standards](/docs/governance/content-standards/tasks/list_content_standards) - List available standards configurations +- [calibrate_content](./calibrate_content) - Collaborative calibration against these standards +- [list_content_standards](./list_content_standards) - List available standards configurations diff --git a/v2.6-rc/docs/governance/content-standards/tasks/get_media_buy_artifacts.mdx b/docs/governance/content-standards/tasks/get_media_buy_artifacts.mdx similarity index 95% rename from v2.6-rc/docs/governance/content-standards/tasks/get_media_buy_artifacts.mdx rename to docs/governance/content-standards/tasks/get_media_buy_artifacts.mdx index 7b430da991..ca702ddb03 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/get_media_buy_artifacts.mdx +++ b/docs/governance/content-standards/tasks/get_media_buy_artifacts.mdx @@ -200,6 +200,6 @@ print(f"False positive rate: {false_positives / len(failures['artifacts']):.1%}" ## Related Tasks -- [validate_content_delivery](/docs/governance/content-standards/tasks/validate_content_delivery) - Validate the artifacts -- [calibrate_content](/docs/governance/content-standards/tasks/calibrate_content) - Understand why artifacts pass/fail -- [get_media_buy_delivery](/docs/media-buy/task-reference/get_media_buy_delivery) - Get performance metrics +- [validate_content_delivery](./validate_content_delivery) - Validate the artifacts +- [calibrate_content](./calibrate_content) - Understand why artifacts pass/fail +- [get_media_buy_delivery](../../../media-buy/task-reference/get_media_buy_delivery) - Get performance metrics diff --git a/v2.6-rc/docs/governance/content-standards/tasks/list_content_standards.mdx b/docs/governance/content-standards/tasks/list_content_standards.mdx similarity index 71% rename from v2.6-rc/docs/governance/content-standards/tasks/list_content_standards.mdx rename to docs/governance/content-standards/tasks/list_content_standards.mdx index cdaea651ae..3110272a68 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/list_content_standards.mdx +++ b/docs/governance/content-standards/tasks/list_content_standards.mdx @@ -15,15 +15,15 @@ List available content standards configurations. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| -| `countries` | array | No | Filter by country codes | +| `countries` | array | No | Filter by ISO 3166-1 alpha-2 country codes (case-insensitive) | | `channels` | array | No | Filter by channels | -| `languages` | array | No | Filter by BCP 47 language tags | +| `languages` | array | No | Filter by ISO 639-1 or BCP 47 language tags (case-insensitive) | ## Response **Schema**: [list-content-standards-response.json](https://adcontextprotocol.org/schemas/v2/content-standards/list-content-standards-response.json) -Returns an abbreviated list of standards configurations. Use [get_content_standards](/docs/governance/content-standards/tasks/get_content_standards) to retrieve full details including policy text and calibration data. +Returns an abbreviated list of standards configurations. Use [get_content_standards](./get_content_standards) to retrieve full details including policy text and calibration data. ### Success Response @@ -63,5 +63,5 @@ Returns an abbreviated list of standards configurations. Use [get_content_standa ## Related Tasks -- [get_content_standards](/docs/governance/content-standards/tasks/get_content_standards) - Get a specific standards configuration -- [create_content_standards](/docs/governance/content-standards/tasks/create_content_standards) - Create a new configuration +- [get_content_standards](./get_content_standards) - Get a specific standards configuration +- [create_content_standards](./create_content_standards) - Create a new configuration diff --git a/v2.6-rc/docs/governance/content-standards/tasks/update_content_standards.mdx b/docs/governance/content-standards/tasks/update_content_standards.mdx similarity index 84% rename from v2.6-rc/docs/governance/content-standards/tasks/update_content_standards.mdx rename to docs/governance/content-standards/tasks/update_content_standards.mdx index 4f2fb5bc29..67227e7488 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/update_content_standards.mdx +++ b/docs/governance/content-standards/tasks/update_content_standards.mdx @@ -67,6 +67,6 @@ Update an existing content standards configuration. Creates a new version. ## Related Tasks -- [get_content_standards](/docs/governance/content-standards/tasks/get_content_standards) - Get current configuration -- [create_content_standards](/docs/governance/content-standards/tasks/create_content_standards) - Create a new configuration -- [delete_content_standards](/docs/governance/content-standards/tasks/delete_content_standards) - Delete a configuration +- [get_content_standards](./get_content_standards) - Get current configuration +- [create_content_standards](./create_content_standards) - Create a new configuration +- [delete_content_standards](./delete_content_standards) - Delete a configuration diff --git a/v2.6-rc/docs/governance/content-standards/tasks/validate_content_delivery.mdx b/docs/governance/content-standards/tasks/validate_content_delivery.mdx similarity index 93% rename from v2.6-rc/docs/governance/content-standards/tasks/validate_content_delivery.mdx rename to docs/governance/content-standards/tasks/validate_content_delivery.mdx index 97f36c2b71..4dfbdfa91a 100644 --- a/v2.6-rc/docs/governance/content-standards/tasks/validate_content_delivery.mdx +++ b/docs/governance/content-standards/tasks/validate_content_delivery.mdx @@ -178,6 +178,6 @@ for result in response["results"]: ## Related Tasks -- [get_media_buy_artifacts](/docs/governance/content-standards/tasks/get_media_buy_artifacts) - Get content artifacts from seller -- [calibrate_content](/docs/governance/content-standards/tasks/calibrate_content) - Understand why artifacts pass/fail -- [get_content_standards](/docs/governance/content-standards/tasks/get_content_standards) - Retrieve the policies +- [get_media_buy_artifacts](./get_media_buy_artifacts) - Get content artifacts from seller +- [calibrate_content](./calibrate_content) - Understand why artifacts pass/fail +- [get_content_standards](./get_content_standards) - Retrieve the policies diff --git a/v2.6-rc/docs/governance/index.mdx b/docs/governance/index.mdx similarity index 95% rename from v2.6-rc/docs/governance/index.mdx rename to docs/governance/index.mdx index f08e368f2d..d0f0e2581b 100644 --- a/v2.6-rc/docs/governance/index.mdx +++ b/docs/governance/index.mdx @@ -19,8 +19,8 @@ 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 | +| **[Property Governance](./property/index)** | Where ads can run | Property lists, compliance filtering, adagents.json authorization | +| **[Brand Standards](./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 | @@ -173,7 +173,7 @@ The Media Buy Protocol consumes governance data at multiple stages: 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 +4. See the [Property Protocol Specification](./property/specification) for detailed implementation guidance ## Protocol Sections diff --git a/v2.6-rc/docs/governance/property/adagents.mdx b/docs/governance/property/adagents.mdx similarity index 80% rename from v2.6-rc/docs/governance/property/adagents.mdx rename to docs/governance/property/adagents.mdx index 2732d1d8f1..7c94641d59 100644 --- a/v2.6-rc/docs/governance/property/adagents.mdx +++ b/docs/governance/property/adagents.mdx @@ -65,6 +65,7 @@ The file must be valid JSON with UTF-8 encoding and return HTTP 200 status. - **`domain`** *(optional)*: Primary domain of managing entity - **`seller_id`** *(optional)*: Seller ID from IAB Tech Lab sellers.json - **`tag_id`** *(optional)*: TAG Certified Against Fraud ID + - **`privacy_policy_url`** *(optional)*: URL to entity's privacy policy for consumer consent flows **`properties`** *(optional)*: Array of properties covered by this file (canonical property definitions) @@ -78,6 +79,14 @@ The file must be valid JSON with UTF-8 encoding and return HTTP 200 status. **`last_updated`** *(optional)*: ISO 8601 timestamp of last modification +**`property_features`** *(optional)*: Array of governance agents that provide data about properties in this file + - **`url`** *(required)*: Agent's API endpoint URL (governance agent implementing property governance tasks) + - **`name`** *(required)*: Human-readable name of the vendor/agent (e.g., "Scope3", "TAG", "OneTrust") + - **`features`** *(required)*: Array of feature IDs this agent provides (e.g., `["carbon_score", "gdpr_compliant"]`) + - **`publisher_id`** *(optional)*: Publisher's identifier at this agent (for lookup) + +This field enables **governance agent discovery** - buyers can find which agents have compliance, sustainability, or quality data for properties without querying every possible agent. + ## URL Reference Pattern For publishers with complex infrastructure or CDN distribution, `adagents.json` can reference an authoritative URL instead of containing the full structure inline. @@ -357,7 +366,8 @@ Large network using tags for grouping efficiency: "email": "adops@meta.com", "domain": "meta.com", "seller_id": "pub-meta-12345", - "tag_id": "12345" + "tag_id": "12345", + "privacy_policy_url": "https://www.meta.com/privacy/policy" }, "properties": [ { @@ -458,6 +468,121 @@ Different agents for different channels: } ``` +### Example 3: Publisher with Governance Agent References + +Publishers can declare which governance agents have data about their properties using `property_features`. This enables buyers to discover where to get compliance, sustainability, and quality data. + +```json +{ + "$schema": "https://adcontextprotocol.org/schemas/v2/adagents.json", + "contact": { + "name": "Premium News Publisher", + "email": "adops@news.example.com", + "domain": "news.example.com" + }, + "properties": [ + { + "property_id": "news_main", + "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" +} +``` + +**Why this works**: +- Publishers declare relationships with governance agents upfront +- Buyers discover governance agents by reading adagents.json (no need to query every possible agent) +- The `publisher_id` field helps agents look up the publisher's data efficiently +- Feature IDs tell buyers what data types are available without querying + +## Governance Agent Discovery + +The `property_features` field solves a key discovery problem: how does a buyer know which governance agents have data about a given property? + +```mermaid +sequenceDiagram + participant Buyer as Buyer Agent + participant PubDomain as Publisher Domain + participant Scope3 as Scope3 Agent + participant OneTrust as OneTrust Agent + + Buyer->>PubDomain: GET /.well-known/adagents.json + PubDomain-->>Buyer: adagents.json with property_features + + Note over Buyer: Extract governance agents from property_features + + par Query governance agents + Buyer->>Scope3: list_property_features + Scope3-->>Buyer: Available features (carbon_score, etc.) + and + Buyer->>OneTrust: list_property_features + OneTrust-->>Buyer: Available features (gdpr_compliant, etc.) + end + + Note over Buyer: Create property lists on each governance agent + + Buyer->>Scope3: create_property_list(filters, brand_manifest) + Buyer->>OneTrust: create_property_list(filters, brand_manifest) +``` + +### When to Use property_features + +| Scenario | Use property_features? | +|----------|------------------------| +| Publisher has carbon scoring from Scope3 | βœ… Yes | +| Publisher is TAG certified | βœ… Yes | +| Publisher tracks consent via OneTrust | βœ… Yes | +| Publisher self-reports brand safety | ❌ No - use property tags | +| Sales agent provides quality data | ❌ No - that's agent capability | + +### Vendor Extensions + +Governance agents can include vendor-specific data in feature definitions via an `ext` block. See [list_property_features](/docs/governance/property/tasks/list_property_features#vendor-extensions) for details. + ## Fetching and Validating ### Using the AdAgents.json Builder @@ -628,6 +753,6 @@ After implementing adagents.json validation: ## Learn More - [AdCP Basics: Authorized Properties](https://bokonads.com/p/adcp-basics-authorized-properties) - Accessible introduction to AdCP authorization -- [list_authorized_properties](/docs/media-buy/task-reference/list_authorized_properties) - Discover publisher domains an agent represents +- [get_adcp_capabilities](/docs/protocol/get_adcp_capabilities) - Discover agent capabilities and portfolio - [Property Schema](https://adcontextprotocol.org/schemas/v2/core/property.json) - Property definition structure - [AdAgents.json Builder](https://adcontextprotocol.org/adagents) - Web-based validator and creator diff --git a/v2.6-rc/docs/governance/property/authorized-properties.mdx b/docs/governance/property/authorized-properties.mdx similarity index 80% rename from v2.6-rc/docs/governance/property/authorized-properties.mdx rename to docs/governance/property/authorized-properties.mdx index 37981ec9e5..5541e0a11c 100644 --- a/v2.6-rc/docs/governance/property/authorized-properties.mdx +++ b/docs/governance/property/authorized-properties.mdx @@ -34,7 +34,7 @@ AdCP faces similar challenges as AI agents begin to buy and sell advertising pro AdCP prevents unauthorized resale through a two-part system: 1. **Publisher Authorization**: Publishers explicitly authorize sales agents via `adagents.json` -2. **Agent Discovery**: Sales agents declare their authorized properties via `list_authorized_properties` +2. **Agent Discovery**: Sales agents declare their portfolio via [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) in the `media_buy.portfolio` section This creates a verifiable chain of authorization that buyer agents can validate. @@ -93,11 +93,15 @@ Publishers authorize sales agents by hosting an `adagents.json` file at `/.well- ## How Sales Agents Share Authorized Properties -Sales agents use the [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) task to declare all properties they are authorized to represent. This serves multiple purposes: +Sales agents use the [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) task to declare their portfolio information in the `media_buy.portfolio` section. This serves multiple purposes: -1. **Transparency**: Buyers can see what properties an agent represents -2. **Validation enablement**: Provides the data buyers need to verify authorization -3. **Tag resolution**: Enables efficient grouping of properties by tags +1. **Transparency**: Buyers can see what publishers an agent represents +2. **Validation enablement**: Provides publisher domains for buyers to verify authorization via `adagents.json` +3. **Portfolio overview**: Includes primary channels, countries, and portfolio description + + +The [`list_authorized_properties`](../../media-buy/task-reference/list_authorized_properties) task is deprecated. Use `get_adcp_capabilities` for portfolio information and fetch detailed property definitions from publisher `adagents.json` files. + ### Property Declaration Example @@ -143,8 +147,8 @@ Sales agents use the [`list_authorized_properties`](/docs/media-buy/task-referen For large networks representing thousands of properties, AdCP supports **property tags** to make the system manageable: - **Products** can reference `["local_radio", "midwest"]` instead of listing hundreds of stations -- **Buyers** use `list_authorized_properties` to resolve tags to actual properties -- **Authorization validation** works on the resolved properties +- **Buyers** use `get_adcp_capabilities` to discover the agent's portfolio and validate authorization +- **Authorization validation** works on the resolved properties via `adagents.json` ## Authorization Validation Workflow @@ -152,15 +156,15 @@ Here's how a buyer agent validates that a sales agent is authorized to represent ### 1. One-Time Setup ```javascript -// Get all properties the sales agent claims to represent -const response = await salesAgent.call('list_authorized_properties'); -const allProperties = response.properties; +// Get portfolio information from capabilities +const capabilities = await salesAgent.call('get_adcp_capabilities'); +const portfolio = capabilities.media_buy?.portfolio; +const publisherDomains = portfolio?.publisher_domains || []; -// For each unique publisher domain, fetch and cache adagents.json -const domains = [...new Set(allProperties.map(p => p.publisher_domain))]; +// For each publisher domain, fetch and cache adagents.json const authorizationCache = {}; -for (const domain of domains) { +for (const domain of publisherDomains) { try { const adagents = await fetch(`https://${domain}/.well-known/adagents.json`); authorizationCache[domain] = await adagents.json(); @@ -174,7 +178,8 @@ for (const domain of domains) { ### 2. Product Validation ```javascript // When evaluating a product -const product = await salesAgent.call('get_products', {brief: "Chicago radio ads"}); +const result = await salesAgent.call('get_products', {brief: "Chicago radio ads"}); +const product = result.products[0]; // Validate authorization for each publisher in publisher_properties const authorized = product.publisher_properties.every(pubProp => { @@ -183,19 +188,10 @@ const authorized = product.publisher_properties.every(pubProp => { if (!adagents) return false; // No adagents.json found - // Get properties from this publisher - const publisherProps = allProperties.filter(p => p.publisher_domain === domain); - - // Resolve property_tags to actual properties if needed - let propertiesToCheck = pubProp.property_ids - ? publisherProps.filter(p => pubProp.property_ids.includes(p.property_id)) - : publisherProps.filter(p => pubProp.property_tags.every(tag => p.tags.includes(tag))); - - // Validate authorization for these properties - return propertiesToCheck.every(property => - adagents.authorized_agents.some(agent => - agent.agent_id === salesAgent.id && - isCurrentlyAuthorized(agent.authorization_scope) + // Verify the sales agent is in publisher's authorized_agents + return adagents.authorized_agents.some(agent => + agent.url === salesAgent.url && + isAuthorizedForProperties(agent, pubProp) ); }); @@ -248,9 +244,9 @@ if (!authorized) { ## Integration with Product Discovery -Authorization validation integrates seamlessly with [Product Discovery](/docs/media-buy/product-discovery/): +Authorization validation integrates seamlessly with [Product Discovery](../../media-buy/product-discovery/): -1. **Discover products** using [`get_products`](/docs/media-buy/task-reference/get_products) +1. **Discover products** using [`get_products`](../../media-buy/task-reference/get_products) 2. **Validate authorization** for properties referenced in products 3. **Proceed confidently** with authorized inventory 4. **Flag unauthorized** products for manual review @@ -269,11 +265,12 @@ 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/governance/property/adagents)** for complete implementation guidance. +See the **[adagents.json Tech Spec](./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 +- **[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)** - Discover agent capabilities and portfolio information +- **[`list_authorized_properties`](../../media-buy/task-reference/list_authorized_properties)** - Property discovery (deprecated - use `get_adcp_capabilities`) +- **[Product Discovery](../../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/governance/property/adagents)** - Complete `adagents.json` implementation guide \ No newline at end of file +- **[adagents.json Tech Spec](./adagents)** - Complete `adagents.json` implementation guide \ No newline at end of file diff --git a/v2.6-rc/docs/governance/property/index.mdx b/docs/governance/property/index.mdx similarity index 61% rename from v2.6-rc/docs/governance/property/index.mdx rename to docs/governance/property/index.mdx index d4f8468777..6b01207c71 100644 --- a/v2.6-rc/docs/governance/property/index.mdx +++ b/docs/governance/property/index.mdx @@ -11,20 +11,20 @@ Property Governance standardizes how advertising properties (websites, apps, CTV ## Overview -Property Governance addresses four distinct concerns: +Property Governance addresses five 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 Data** | What do we know about this property? | Data providers | Governance agents via [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) | | **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. +The first three 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`: +Publishers declare their properties, authorize sales agents, and reference governance agents via `/.well-known/adagents.json`: ```json { @@ -44,25 +44,59 @@ Publishers declare their properties and authorize sales agents via `/.well-known "authorization_type": "property_ids", "property_ids": ["example_site"] } + ], + "property_features": [ + { + "url": "https://api.scope3.com", + "name": "Scope3", + "features": ["carbon_score", "sustainability_grade"] + }, + { + "url": "https://api.onetrust.com", + "name": "OneTrust", + "features": ["gdpr_compliant", "tcf_registered"] + } ] } ``` -See the [adagents.json Tech Spec](/docs/governance/property/adagents) for complete documentation. +### Governance Agent Discovery via property_features + +The `property_features` array solves a key discovery problem: **how does a buyer know which governance agents have data about a given property?** + +Without `property_features`, buyers would need to query every possible governance agent to find out who has compliance, sustainability, or quality data. With `property_features`, publishers declare these relationships upfront: + +| Field | Purpose | +|-------|---------| +| `url` | Governance agent's API endpoint | +| `name` | Human-readable agent name (e.g., "Scope3", "TAG", "OneTrust") | +| `features` | Feature IDs this agent provides (e.g., `carbon_score`, `gdpr_compliant`) | +| `publisher_id` | Optional identifier for looking up this publisher at the agent | + +**Example use cases:** +- **Sustainability**: Publisher declares Scope3 tracks their carbon emissions +- **Compliance**: Publisher declares OneTrust manages their consent data +- **Certification**: Publisher declares TAG has verified their fraud prevention + +Buyers read `property_features` from adagents.json, then query only the relevant governance agents for detailed data. + +See the [adagents.json Tech Spec](/docs/governance/property/adagents) for complete documentation including examples and the discovery workflow. ## 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`: +Governance agents provide data about properties - compliance scores, brand safety ratings, sustainability metrics, etc. They advertise their capabilities via [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) in the `governance.property_features` section: ```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 } } - ] + "governance": { + "property_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 } } + ] + } } ``` @@ -113,8 +147,14 @@ A buyer agent typically works with **multiple governance agents** (consent, bran ```mermaid flowchart TB + subgraph Publisher["PUBLISHER (adagents.json)"] + P1["properties: identity"] + P2["authorized_agents: sales auth"] + P3["property_features: governance refs"] + end + subgraph Buyer["BUYER AGENT"] - B1[Source of truth for compliant list] + B1[Discovers governance agents from adagents.json] B2[Aggregates results from specialized agents] B3[Issues auth_tokens for sellers] end @@ -130,6 +170,7 @@ flowchart TB SE2[Uses cached lists for bid-time decisions] end + Publisher -->|property_features discovery| Buyer Buyer -->|create_property_list + webhooks| CA Buyer -->|create_property_list + webhooks| S3 Buyer -->|create_property_list + webhooks| BS @@ -137,6 +178,17 @@ flowchart TB Buyer -->|get_property_list with auth_token| Seller ``` +### The Complete Flow + +1. **Publisher declares** properties, sales agents, AND governance agents in `adagents.json` +2. **Buyer discovers** governance agents by reading `property_features` from adagents.json +3. **Buyer queries** each governance agent's `list_property_features` for detailed capabilities +4. **Buyer creates** property lists on each governance agent with filters and brand manifests +5. **Governance agents evaluate** properties and notify buyer via webhooks when lists change +6. **Buyer aggregates** results into a final compliant list +7. **Buyer shares** property list reference with sellers (with auth token) +8. **Seller caches** resolved list for bid-time decisions + ## Sharing Property Lists with Sellers Once a buyer has a compliant property list, they share it with sellers: @@ -197,7 +249,8 @@ Both protocols operate on properties but serve different purposes: ### Discovery -- **[list_property_features](/docs/governance/property/tasks/list_property_features)**: Discover what data a governance agent can provide about properties +- **[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)**: Discover governance capabilities including property features (protocol-level task) +- **[list_property_features](./tasks/list_property_features)**: Discover what data a governance agent can provide (deprecated - use `get_adcp_capabilities`) ### Property List Management @@ -211,17 +264,20 @@ Both protocols operate on properties but serve different purposes: **Publishers:** 1. Create `/.well-known/adagents.json` with property definitions 2. Authorize sales agents for your properties +3. Declare governance agents in `property_features` (Scope3 for carbon, OneTrust for consent, TAG for certification, etc.) **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) +1. Discover governance agents by reading `property_features` from publishers' adagents.json files +2. Query each governance agent's `list_property_features` for capabilities +3. Create property lists on relevant governance agents with filters and brand manifests +4. Aggregate results into a final compliant list +5. Share property list references with sellers (with auth tokens) **Governance Agent Implementers:** -1. Implement `list_property_features` to advertise your capabilities +1. Implement [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) to advertise your capabilities in `governance.property_features` 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 +4. Work with publishers to get listed in their `property_features` +5. 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/v2.6-rc/docs/governance/property/specification.mdx b/docs/governance/property/specification.mdx similarity index 95% rename from v2.6-rc/docs/governance/property/specification.mdx rename to docs/governance/property/specification.mdx index 343d1c7e32..15dee4fede 100644 --- a/v2.6-rc/docs/governance/property/specification.mdx +++ b/docs/governance/property/specification.mdx @@ -192,7 +192,7 @@ Discover what features a governance agent can evaluate. 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 country in `countries_all` (ISO 3166-1 alpha-2 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: @@ -414,6 +414,36 @@ Orchestrators may consult multiple governance agents: ## Agent Discovery +There are two complementary discovery mechanisms: + +### Publisher-Side Discovery via adagents.json + +Publishers declare which governance agents have data about their properties using the `property_features` field in `adagents.json`: + +```json +{ + "property_features": [ + { + "url": "https://api.scope3.com", + "name": "Scope3", + "features": ["carbon_score", "sustainability_grade"], + "publisher_id": "pub_12345" + }, + { + "url": "https://api.onetrust.com", + "name": "OneTrust", + "features": ["gdpr_compliant", "tcf_registered", "ccpa_compliant"] + } + ] +} +``` + +This solves the discovery problem: buyers don't need to query every possible governance agent. Instead, they read `property_features` from the publisher's adagents.json to find which agents have relevant data. + +See the [adagents.json Tech Spec](/docs/governance/property/adagents#governance-agent-discovery) for the complete discovery workflow. + +### Agent-Side Discovery via agent-card.json + Governance agents expose capabilities via `.well-known/agent-card.json`: ```json @@ -444,6 +474,8 @@ Governance agents expose capabilities via `.well-known/agent-card.json`: } ``` +### Detailed Capability Discovery + Use `list_property_features` for detailed capability discovery: ```json diff --git a/v2.6-rc/docs/governance/property/tasks/index.mdx b/docs/governance/property/tasks/index.mdx similarity index 74% rename from v2.6-rc/docs/governance/property/tasks/index.mdx rename to docs/governance/property/tasks/index.mdx index 4679e3704b..067a9301d1 100644 --- a/v2.6-rc/docs/governance/property/tasks/index.mdx +++ b/docs/governance/property/tasks/index.mdx @@ -15,27 +15,29 @@ Property governance uses a **stateful** model where all evaluation happens throu | Task | Purpose | Response Time | |------|---------|---------------| -| [list_property_features](/docs/governance/property/tasks/list_property_features) | Discover agent capabilities | ~200ms | +| [get_adcp_capabilities](/docs/protocol/get_adcp_capabilities) | Discover agent capabilities | ~200ms | + +Use the protocol-level `get_adcp_capabilities` task to discover what features a governance agent can evaluate. See the [governance section](/docs/protocol/get_adcp_capabilities#governance-protocol) for details on the `property_features` array. ## 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 | +| [create_property_list](./property_lists#create_property_list) | Create a new property list | ~500ms | +| [update_property_list](./property_lists#update_property_list) | Modify an existing list | ~500ms | +| [get_property_list](./property_lists#get_property_list) | Retrieve list with resolved properties | ~2-5s | +| [list_property_lists](./property_lists#list_property_lists) | List all property lists | ~500ms | +| [delete_property_list](./property_lists#delete_property_list) | Delete a property list | ~200ms | -See [Property List Management](/docs/governance/property/tasks/property_lists) for complete CRUD documentation. +See [Property List Management](./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 | +| [validate_property_delivery](./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. +See [validate_property_delivery](./validate_property_delivery) for post-campaign compliance validation. ## Task Selection Guide @@ -92,7 +94,7 @@ Use `create_property_list` with filters and brand manifest: - `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`. +Filters have two built-in fields (`countries_all`, `channels_any`) plus `feature_requirements` which reference features the agent provides (discovered via [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)). For quantitative features, use `min_value`/`max_value`. For binary or categorical features, use `allowed_values`. ### Getting Resolved Properties 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 0000000000..c24c1ccfa4 --- /dev/null +++ b/docs/governance/property/tasks/list_property_features.mdx @@ -0,0 +1,200 @@ +--- +sidebar_position: 1 +title: list_property_features +--- + + +**Deprecated** - Use [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) instead. Governance agent capabilities are now declared in the `governance.property_features` section of the capabilities response. + + +This task has been superseded by the protocol-level [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) task, which provides capability discovery across all AdCP protocols in a single call. + +## Migration + +Instead of calling `list_property_features`: + +```json +{ + "tool": "list_property_features", + "arguments": {} +} +``` + +Call `get_adcp_capabilities` and access the `governance.property_features` array: + +```json +{ + "tool": "get_adcp_capabilities", + "arguments": {} +} +``` + +Response: + +```json +{ + "adcp": { "major_versions": [1] }, + "supported_protocols": ["governance"], + "governance": { + "property_features": [ + { + "feature_id": "consent_quality", + "type": "quantitative", + "range": { "min": 0, "max": 100 }, + "description": "Measures the quality of consent implementation" + }, + { + "feature_id": "coppa_certified", + "type": "binary", + "description": "Whether the property has COPPA certification" + }, + { + "feature_id": "content_category", + "type": "categorical", + "categories": ["news", "sports", "entertainment", "technology"], + "description": "IAB content category classification" + } + ] + } +} +``` + +## 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 | `categories: string[]` | + +## 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" } + ] +} +``` + +## Vendor Extensions + +Feature definitions can include vendor-specific data in an `ext` block with vendor-namespaced fields: + +```json +{ + "feature_id": "carbon_score", + "name": "Carbon Emissions Score", + "type": "quantitative", + "range": { "min": 0, "max": 100 }, + "methodology_url": "https://scope3.com/methodology", + "ext": { + "scope3": { + "emissions_unit": "gCO2e_per_1000_impressions", + "model_version": "2024.1", + "sbti_aligned": true + } + } +} +``` + +Extensions are optional. Buyers should process features normally even if `ext` contains unfamiliar vendors. Each vendor's `methodology_url` documents their extension schema. + +## 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 +) +``` + +## Discovering Governance Agents + +Before calling `list_property_features`, you need to know which governance agents to query. There are two discovery mechanisms: + +### Via adagents.json (Publisher-Declared) + +Publishers declare which governance agents have data about their properties in `property_features` (from their `/.well-known/adagents.json`): + +```json +{ + "property_features": [ + { + "url": "https://api.scope3.com", + "name": "Scope3", + "features": ["carbon_score", "sustainability_grade"] + } + ] +} +``` + +This tells buyers: "Scope3 has carbon data for our properties." The buyer then calls `list_property_features` on Scope3 to get detailed feature definitions. + +See the [adagents.json Tech Spec](/docs/governance/property/adagents#governance-agent-discovery) for the complete discovery workflow. + +### Via Direct Subscription + +Buyers may also have direct relationships with governance agents (e.g., a subscription to a compliance vendor). In this case, the buyer already knows which agents to query. + +## Usage Notes + +1. **Discover first**: Find governance agents via `property_features` in adagents.json before querying +2. **Call early**: Use this task to understand capabilities before other governance tasks +3. **Cache results**: Feature definitions change infrequently; cache for hours/days +4. **Check coverage**: Not all features apply to all property types or countries +5. **Marketplace discovery**: Use this to understand what different governance agents offer diff --git a/v2.6-rc/docs/governance/property/tasks/property_lists.mdx b/docs/governance/property/tasks/property_lists.mdx similarity index 98% rename from v2.6-rc/docs/governance/property/tasks/property_lists.mdx rename to docs/governance/property/tasks/property_lists.mdx index a46e54545f..4faef62b2c 100644 --- a/v2.6-rc/docs/governance/property/tasks/property_lists.mdx +++ b/docs/governance/property/tasks/property_lists.mdx @@ -168,7 +168,7 @@ 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) | +| `countries_all` | string[] | ISO 3166-1 alpha-2 country codes (case-insensitive, uppercase recommended) - 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 | @@ -229,7 +229,7 @@ This transparency helps agencies distinguish between properties that passed a re ### Required Filters Every property list must include at least: -- One country in `countries_all` (ISO country code) +- One country in `countries_all` (ISO 3166-1 alpha-2 code, case-insensitive) - 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. diff --git a/v2.6-rc/docs/governance/property/tasks/validate_property_delivery.mdx b/docs/governance/property/tasks/validate_property_delivery.mdx similarity index 97% rename from v2.6-rc/docs/governance/property/tasks/validate_property_delivery.mdx rename to docs/governance/property/tasks/validate_property_delivery.mdx index 7f9acc8c42..1e0fe14594 100644 --- a/v2.6-rc/docs/governance/property/tasks/validate_property_delivery.mdx +++ b/docs/governance/property/tasks/validate_property_delivery.mdx @@ -397,6 +397,6 @@ def analyze_validation(response): ## 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 +- [create_property_list](./property_lists#create_property_list) - Create the list to validate against +- [get_property_list](./property_lists#get_property_list) - Retrieve current list membership +- [list_property_features](./list_property_features) - Discover available filter features diff --git a/docs/intro.mdx b/docs/intro.mdx index 5ee923163e..91db625347 100644 --- a/docs/intro.mdx +++ b/docs/intro.mdx @@ -7,9 +7,9 @@ keywords: [advertising automation protocol, programmatic advertising API, MCP ad -**πŸŽ‰ AdCP v2.5.0 Released** +**πŸš€ AdCP v3.0.0-beta.1 Available** -Developer experience and API refinement release featuring type safety improvements, batch creative previews (5-10x faster), schema versioning (`/schemas/v2.5/`), template formats with dynamic sizing, enhanced product filtering, and inline creative updates. [See what's new β†’](/docs/reference/release-notes) +Major release with breaking changes: new Media Channel Taxonomy (19 planning-oriented channels), creative assignments with weighting, and `get_adcp_capabilities` for protocol-level discovery. Plus new Governance Protocol for property lists and content standards. [See what's new β†’](/docs/reference/release-notes) Welcome to the Ad Context Protocol (AdCP) documentationβ€”the open standard for agentic advertising. @@ -260,6 +260,14 @@ AdCP's task-first architecture means you can access the same functionality throu - **Using A2A**: Perfect for complex workflows with approvals and multi-agent collaboration - **Protocol Agnostic**: Implementers write tasks once, support all protocols automatically +### Capability Discovery + +Every AdCP agent supports [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) as the primary entry point for discovering what the agent supports. This single call returns: +- Protocol versions and supported domains (media_buy, signals, governance) +- Portfolio information for sales agents +- Geo targeting capabilities and execution features +- Supported extensions for rich metadata + Learn more in the [Protocols section](/docs/protocols/getting-started). ## Next Steps diff --git a/docs/media-buy/advanced-topics/testing.mdx b/docs/media-buy/advanced-topics/testing.mdx index 3eb46502cf..90dc03a3ba 100644 --- a/docs/media-buy/advanced-topics/testing.mdx +++ b/docs/media-buy/advanced-topics/testing.mdx @@ -60,7 +60,7 @@ Addie can run comprehensive E2E tests including: **Standard Scenarios:** - **health_check** - Verify agent responds -- **discovery** - Test `get_products`, `list_creative_formats`, `list_authorized_properties` +- **discovery** - Test `get_adcp_capabilities`, `get_products`, `list_creative_formats` - **create_media_buy** - Discovery + create a test campaign - **full_sales_flow** - Complete lifecycle: create β†’ update β†’ delivery - **creative_sync** - Test `sync_creatives` flow @@ -83,9 +83,9 @@ By default tests run in dry-run mode. For real testing, ask Addie to run without Use this checklist to verify your sales agent implementation covers all required features: **Core Discovery (Required)** +- [ ] `get_adcp_capabilities` - Returns agent capabilities, portfolio, and supported features - [ ] `get_products` - Returns products with pricing_options, format_ids, delivery_type - [ ] `list_creative_formats` - Returns supported formats and creative agents -- [ ] `list_authorized_properties` - Returns publisher domains (if applicable) **Media Buy Lifecycle (Required)** - [ ] `create_media_buy` - Accepts packages with product_id, pricing_option_id, budget diff --git a/docs/media-buy/capability-discovery/index.mdx b/docs/media-buy/capability-discovery/index.mdx index 885a0e16c9..a7249397f7 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. @@ -38,21 +38,25 @@ Learn how AdCP prevents unauthorized resale and ensures sales agents are legitim These capability discovery tasks provide the reference data needed for effective AdCP workflows: +### [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) +The primary capability discovery task. Returns protocol versions, supported features, portfolio information, and governance capabilities in a single call. Use this as your first interaction with any AdCP agent. + ### [`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats) Discover all supported creative formats with detailed specifications including dimensions, file types, duration limits, and technical requirements. -### [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) -Get all properties a sales agent is authorized to represent, including property tags for efficient organization and authorization validation data. +### [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) deprecated +Get all properties a sales agent is authorized to represent. **Deprecated** - use `get_adcp_capabilities` for portfolio information via the `media_buy.portfolio` section. ## Integration Pattern Capability discovery typically happens early in your AdCP workflow: -1. **Understand Formats**: Call [`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats) to learn supported creative types -2. **Validate Authorization**: Use [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) to verify sales agent legitimacy -3. **Discover Products**: Search for advertising inventory with [`get_products`](/docs/media-buy/task-reference/get_products) -4. **Plan Creatives**: Match discovered products to available formats for production planning -5. **Execute Campaigns**: Create media buys with confidence in format compatibility and authorization +1. **Discover Capabilities**: Call [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) to learn what the agent supports +2. **Understand Formats**: Call [`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats) to learn supported creative types +3. **Validate Authorization**: Check the `media_buy.portfolio` from capabilities, then verify via publisher `adagents.json` +4. **Discover Products**: Search for advertising inventory with [`get_products`](/docs/media-buy/task-reference/get_products) +5. **Plan Creatives**: Match discovered products to available formats for production planning +6. **Execute Campaigns**: Create media buys with confidence in format compatibility and authorization ## Why This Matters @@ -80,4 +84,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 ce1c80cf19..a1bc39eddf 100644 --- a/docs/media-buy/index.mdx +++ b/docs/media-buy/index.mdx @@ -23,9 +23,10 @@ All protocols provide identical functionality - choose based on your integration The Media Buy protocol provides these essential operations: ### Discovery & Planning +- **[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)**: Discover agent capabilities, portfolio, and supported features (protocol-level) - **[`get_products`](/docs/media-buy/task-reference/get_products)**: Discover advertising inventory using natural language briefs - **[`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats)**: Understand creative requirements and specifications -- **[`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties)**: Verify publisher authorization and available properties +- **[`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties)**: Verify publisher authorization (deprecated - use `get_adcp_capabilities`) ### Campaign Execution - **[`create_media_buy`](/docs/media-buy/task-reference/create_media_buy)**: Launch advertising campaigns with complete lifecycle management @@ -223,9 +224,10 @@ Choose your path based on your role and needs: ### **For AI Agent Developers** 1. **Start with [Protocol Selection](/docs/protocols/protocol-comparison)** - Choose MCP or A2A based on your use case -2. **Learn [Capability Discovery](/docs/media-buy/capability-discovery/)** - Understand creative formats and property authorization -3. **Try [Product Discovery](/docs/media-buy/product-discovery/)** - See how natural language briefs work -4. **Reference [Task Reference](/docs/media-buy/task-reference/)** - Implement the 8 core tasks +2. **Call [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)** - Discover what the agent supports +3. **Learn [Capability Discovery](/docs/media-buy/capability-discovery/)** - Understand creative formats and property authorization +4. **Try [Product Discovery](/docs/media-buy/product-discovery/)** - See how natural language briefs work +5. **Reference [Task Reference](/docs/media-buy/task-reference/)** - Implement the core tasks ### **For Campaign Managers** 1. **Understand the [Media Buy Lifecycle](/docs/media-buy/media-buys/)** - Learn the complete workflow @@ -234,7 +236,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/create_media_buy.mdx b/docs/media-buy/task-reference/create_media_buy.mdx index 80001c91ba..fa138364b3 100644 --- a/docs/media-buy/task-reference/create_media_buy.mdx +++ b/docs/media-buy/task-reference/create_media_buy.mdx @@ -182,8 +182,8 @@ npx adcp \ | `pacing` | string | No | `"even"` (default), `"asap"`, or `"front_loaded"` | | `bid_price` | number | No | Bid price for auction pricing (required when `is_fixed` is false) | | `targeting_overlay` | TargetingOverlay | No | Additional targeting criteria (see [Targeting](/docs/media-buy/advanced-topics/targeting)) | -| `creative_ids` | string[] | No | Existing library creative IDs to assign | -| `creatives` | CreativeAsset[] | No | Full creative objects to upload and assign | +| `creative_assignments` | CreativeAssignment[] | No | Assign existing library creatives with optional weights and placement targeting | +| `creatives` | CreativeAsset[] | No | Upload new creative assets and assign (`creative_id` must not already exist in library) | ## Response @@ -550,6 +550,7 @@ Common errors and resolutions: | `TARGETING_TOO_NARROW` | Targeting yields zero inventory | Broaden geographic or audience criteria | | `POLICY_VIOLATION` | Brand/product violates policy | Review publisher's content policies | | `INVALID_PRICING_OPTION` | pricing_option_id not found | Use ID from product's `pricing_options` | +| `CREATIVE_ID_EXISTS` | Creative ID already exists in library | Use a different `creative_id`, assign existing creatives via `creative_assignments`, or update via `sync_creatives` | Example error response: @@ -1025,6 +1026,7 @@ For complete async handling patterns, see [Task Management](/docs/protocols/task - AXE segments enable advanced audience targeting - Pending states (`working`, `submitted`) are normal, not errors - Orchestrators MUST handle pending states as part of normal workflow +- **Inline creatives**: The `creatives` array creates NEW creatives only. To update existing creatives, use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives). To assign existing library creatives, use `creative_assignments` instead. ## Policy Compliance diff --git a/docs/media-buy/task-reference/get_products.mdx b/docs/media-buy/task-reference/get_products.mdx index 9a877d146e..2280c722e7 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 @@ -143,8 +144,8 @@ asyncio.run(discover_with_filters()) | `start_date` | string | Campaign start date in ISO 8601 format (YYYY-MM-DD) for availability checks | | `end_date` | string | Campaign end date in ISO 8601 format (YYYY-MM-DD) for availability checks | | `budget_range` | object | Budget range to filter appropriate products (see Budget Range Object below) | -| `countries` | string[] | Filter by target countries using ISO 3166-1 alpha-2 codes (e.g., `["US", "CA", "GB"]`) | -| `channels` | string[] | Filter by advertising channels (e.g., `["display", "video", "dooh", "ctv", "audio", "native"]`) | +| `countries` | string[] | Filter by target countries using ISO 3166-1 alpha-2 codes (case-insensitive, e.g., `["US", "CA", "GB"]`) | +| `channels` | string[] | Filter by advertising channels (e.g., `["display", "ctv", "social", "streaming_audio"]`). See [Media Channel Taxonomy](/docs/reference/media-channel-taxonomy) | ### Budget Range 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 @@ -291,7 +298,7 @@ const result = await testAgent.getProducts({ currency: 'USD' }, countries: ['US', 'CA'], - channels: ['display', 'video', 'ctv'], + channels: ['display', 'ctv', 'streaming_audio'], delivery_type: 'guaranteed' } }); @@ -322,7 +329,7 @@ async def discover_with_budget_and_dates(): 'currency': 'USD' }, 'countries': ['US', 'CA'], - 'channels': ['display', 'video', 'ctv'], + 'channels': ['web', 'ctv', 'streaming_audio'], 'delivery_type': 'guaranteed' } ) @@ -350,7 +357,8 @@ const result = await testAgent.getProducts({ }); if (result.success && result.data) { - // Products with property_tags in publisher_properties need resolution via list_authorized_properties + // Products with property_tags in publisher_properties represent large networks + // Use get_adcp_capabilities to discover the agent's portfolio const productsWithTags = result.data.products.filter(p => p.publisher_properties?.some(pub => pub.property_tags && pub.property_tags.length > 0) ); @@ -372,7 +380,8 @@ async def discover_property_tags(): } ) - # Products with property_tags in publisher_properties need resolution via list_authorized_properties + # Products with property_tags in publisher_properties represent large networks + # Use get_adcp_capabilities to discover the agent's portfolio products_with_tags = [p for p in result.products if any(pub.get('property_tags') for pub in p.get('publisher_properties', []))] print(f"{len(products_with_tags)} products use property tags (large networks)") @@ -476,6 +485,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/index.mdx b/docs/media-buy/task-reference/index.mdx index df786fb95a..12f956cc67 100644 --- a/docs/media-buy/task-reference/index.mdx +++ b/docs/media-buy/task-reference/index.mdx @@ -16,7 +16,7 @@ Complete reference for all AdCP Media Buy tasks. Each task is designed for AI ag | [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) | Create campaigns from selected products | Minutes-Days | Media Buys | | [`update_media_buy`](/docs/media-buy/task-reference/update_media_buy) | Modify campaign settings and budgets | Minutes-Days | Media Buys | | [`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats) | View supported creative specifications | ~1s | Capability | -| [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) | See available publisher properties | ~1s | Capability | +| [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) | See available publisher properties (deprecated) | ~1s | Capability | | [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives) | Upload and manage creative assets | Minutes-Days | Creatives | | [`list_creatives`](/docs/media-buy/task-reference/list_creatives) | Query creative library with filtering | ~1s | Creatives | | [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) | Retrieve performance and delivery data | ~60s | Reporting | @@ -28,7 +28,7 @@ AdCP tasks fall into four response time categories: ### 🟒 Instant (~1 second) **Simple database lookups** - [`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats) - Format specifications -- [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) - Available properties +- [`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties) - Available properties (deprecated - use [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)) - [`list_creatives`](/docs/media-buy/task-reference/list_creatives) - Creative library queries ### 🟑 Processing (~60 seconds) @@ -47,9 +47,10 @@ AdCP tasks fall into four response time categories: ### Discovery & Planning Start here to understand what's available and plan your campaign. +- **[`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)** - Discover agent capabilities, portfolio, and supported features (protocol-level task) - **[`get_products`](/docs/media-buy/task-reference/get_products)** - The core discovery task using natural language briefs - **[`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats)** - Understand creative requirements -- **[`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties)** - See available placements +- **[`list_authorized_properties`](/docs/media-buy/task-reference/list_authorized_properties)** - See available placements (deprecated) ### Media Buy Management Create and manage your advertising campaigns. @@ -102,11 +103,12 @@ Long-running tasks provide: ## Getting Started -1. **Start with Discovery**: Use [`get_products`](/docs/media-buy/task-reference/get_products) to find relevant inventory -2. **Understand Formats**: Check [`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats) for requirements -3. **Create Campaign**: Use [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) with selected products -4. **Upload Creatives**: Use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives) for asset management -5. **Monitor Performance**: Track results with [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) +1. **Discover Capabilities**: Use [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) to understand what the agent supports +2. **Find Inventory**: Use [`get_products`](/docs/media-buy/task-reference/get_products) to find relevant inventory +3. **Understand Formats**: Check [`list_creative_formats`](/docs/media-buy/task-reference/list_creative_formats) for requirements +4. **Create Campaign**: Use [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) with selected products +5. **Upload Creatives**: Use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives) for asset management +6. **Monitor Performance**: Track results with [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) ## Related Documentation diff --git a/docs/media-buy/task-reference/list_authorized_properties.mdx b/docs/media-buy/task-reference/list_authorized_properties.mdx index 5e7cb8ea9d..daff660aea 100644 --- a/docs/media-buy/task-reference/list_authorized_properties.mdx +++ b/docs/media-buy/task-reference/list_authorized_properties.mdx @@ -1,9 +1,14 @@ --- title: list_authorized_properties -sidebar_position: 1.5 +sidebar_position: 1.6 testable: true --- +:::warning Deprecated +`list_authorized_properties` is deprecated. Use [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) instead, which provides expanded capability declaration including protocol versions, execution features, and portfolio information. + +The portfolio fields from this task are available in the `media_buy.portfolio` section of `get_adcp_capabilities` response. +::: Discover which publishers a sales agent is authorized to represent. Returns publisher domains only - buyers fetch full property definitions from each publisher's adagents.json. @@ -28,8 +33,8 @@ Discover which publishers a sales agent is authorized to represent. Returns publ | Field | Description | |-------|-------------| | `publisher_domains` | Array of publisher domains this agent represents | -| `primary_channels` | Optional main advertising channels (ctv, display, video, audio, dooh, etc.) | -| `primary_countries` | Optional main countries (ISO 3166-1 alpha-2 codes) | +| `primary_channels` | Optional main advertising channels (e.g., `display`, `ctv`, `social`, `streaming_audio`). See [Media Channel Taxonomy](/docs/reference/media-channel-taxonomy) | +| `primary_countries` | Optional main countries (ISO 3166-1 alpha-2 codes, case-insensitive) | | `portfolio_description` | Optional markdown description of portfolio and capabilities | | `last_updated` | Optional ISO 8601 timestamp of last publisher list update | @@ -404,7 +409,7 @@ Optional fields provide high-level portfolio information for quick filtering: ### primary_channels Main advertising channels in portfolio: -- Values: `display`, `video`, `dooh`, `ctv`, `podcast`, `retail`, etc. +- Values: `display`, `olv`, `dooh`, `ctv`, `podcast`, `retail_media`, etc. - See [Channels enum](https://adcontextprotocol.org/schemas/v2/enums/channels.json) - Use case: Filter "Do you have DOOH?" before examining properties @@ -435,7 +440,7 @@ Markdown description: **News Publisher**: ```json { - "primary_channels": ["display", "video", "native"], + "primary_channels": ["display", "olv"], "primary_countries": ["US", "GB", "AU"], "portfolio_description": "News and business publisher network. Desktop and mobile web with professional audience." } @@ -498,6 +503,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/media-buy/task-reference/list_creatives.mdx b/docs/media-buy/task-reference/list_creatives.mdx index 1f5c3804de..46e76a9d7d 100644 --- a/docs/media-buy/task-reference/list_creatives.mdx +++ b/docs/media-buy/task-reference/list_creatives.mdx @@ -46,14 +46,16 @@ The `list_creatives` task provides comprehensive search and filtering capabiliti ```json { "filters": { - "format": "video", // Single format - "formats": ["video", "audio"], // Multiple formats - "status": "approved", // Single status - "statuses": ["approved", "pending_review"] // Multiple statuses + "formats": ["video"], // Filter by format types + "statuses": ["approved", "pending_review"] // Filter by statuses } } ``` + +**Archived creatives are excluded by default.** To include archived creatives in results, explicitly filter by `status: "archived"` or include `"archived"` in the `statuses` array. + + ### Tag-Based Filtering ```json @@ -94,8 +96,7 @@ The `list_creatives` task provides comprehensive search and filtering capabiliti ```json { "filters": { - "assigned_to_package": "pkg_ctv_001", // Assigned to specific package - "assigned_to_packages": ["pkg_001", "pkg_002"], // Assigned to any of these packages + "assigned_to_packages": ["pkg_ctv_001"], // Assigned to any of these packages "media_buy_ids": ["mb_123", "mb_456"], // Assigned to any of these media buys "buyer_refs": ["campaign_abc", "campaign_xyz"], // Assigned to media buys with these buyer references "unassigned": true // Unassigned creatives only @@ -192,7 +193,8 @@ The response provides comprehensive creative data with optional enrichment: "status_summary": { "approved": 20, "pending_review": 3, - "rejected": 2 + "rejected": 2, + "archived": 5 } } ``` @@ -206,8 +208,8 @@ List all approved video creatives: ```json { "filters": { - "format": "video", - "status": "approved" + "formats": ["video"], + "statuses": ["approved"] }, "sort": { "field": "created_date", @@ -246,7 +248,7 @@ Get creatives ready for assignment to new campaigns: { "filters": { "unassigned": true, - "status": "approved" + "statuses": ["approved"] }, "sort": { "field": "created_date", @@ -265,7 +267,7 @@ Find all creatives assigned to a specific package: ```json { "filters": { - "assigned_to_package": "pkg_ctv_premium_001" + "assigned_to_packages": ["pkg_ctv_premium_001"] }, "include_assignments": true, "include_performance": true, @@ -285,7 +287,7 @@ Find mobile-optimized creatives across multiple formats: "filters": { "formats": ["display_320x50", "video_9x16_mobile", "display_300x250"], "tags_any": ["mobile_optimized", "responsive"], - "status": "approved" + "statuses": ["approved"] }, "sort": { "field": "updated_date", @@ -303,7 +305,7 @@ Get minimal creative data for UI dropdown: "fields": ["creative_id", "name", "format", "status"], "include_assignments": false, "filters": { - "status": "approved" + "statuses": ["approved"] }, "sort": { "field": "name", @@ -358,7 +360,7 @@ Find all creatives used in specific media buys or campaigns: { "filters": { "media_buy_ids": ["mb_summer_2025", "mb_spring_2025"], - "status": "approved" + "statuses": ["approved"] }, "include_assignments": true, "include_performance": true, @@ -387,6 +389,22 @@ Search creatives across related campaigns by buyer reference: } ``` +### Example 11: Query Archived Creatives + +View archived creatives (excluded from default queries): + +```json +{ + "filters": { + "status": "archived" + }, + "sort": { + "field": "updated_date", + "direction": "desc" + } +} +``` + ## Query Optimization Tips ### 1. Use Specific Filters @@ -429,7 +447,7 @@ Search creatives across related campaigns by buyer reference: ```json { "filters": { - "status": "approved", + "statuses": ["approved"], "unassigned": true }, "sort": { "field": "created_date", "direction": "desc" } @@ -502,7 +520,7 @@ For queries returning very large result sets: // Get lightweight creative list for dropdown const response = await adcp.list_creatives({ fields: ["creative_id", "name", "format"], - filters: { status: "approved" }, + filters: { statuses: ["approved"] }, sort: { field: "name", direction: "asc" }, include_assignments: false }); @@ -525,12 +543,12 @@ const response = await adcp.list_creatives({ ### Assignment Management -```javascript +```javascript // Find unassigned creatives ready for new campaigns const response = await adcp.list_creatives({ filters: { unassigned: true, - status: "approved", + statuses: ["approved"], formats: ["video", "display"] }, sort: { field: "created_date", direction: "desc" } diff --git a/docs/media-buy/task-reference/sync_creatives.mdx b/docs/media-buy/task-reference/sync_creatives.mdx index e5133c59d2..30a5182714 100644 --- a/docs/media-buy/task-reference/sync_creatives.mdx +++ b/docs/media-buy/task-reference/sync_creatives.mdx @@ -106,7 +106,7 @@ asyncio.run(main()) | `assignments` | object | No | Map of creative_id to array of package_ids for bulk assignment | | `dry_run` | boolean | No | When true, preview changes without applying them (default: false) | | `validation_mode` | string | No | Validation strictness: `"strict"` (default) or `"lenient"` | -| `delete_missing` | boolean | No | When true, creatives not in this sync are archived (default: false) | +| `delete_missing` | boolean | No | When true, creatives not in this sync are archived (default: false). Cannot delete creatives assigned to active, non-paused packages. | ### Creative Object @@ -651,11 +651,11 @@ The operation status (`completed`) means the review process finished. Individual | `PACKAGE_NOT_FOUND` | Package ID doesn't exist in media buy | Verify `package_id` from `create_media_buy` response | | `BRAND_SAFETY_VIOLATION` | Creative failed brand safety scan | Review content against publisher's brand safety guidelines | | `FORMAT_MISMATCH` | Assets don't match format requirements | Verify asset types and specifications match format definition | -| `DUPLICATE_CREATIVE_ID` | Creative ID already exists in different media buy | Use unique `creative_id` or sync to correct media buy | +| `CREATIVE_IN_ACTIVE_DELIVERY` | Creative is assigned to an active, non-paused package (blocks updates and `delete_missing` deletions) | Pause the package first, or create a new creative version | ## Best Practices -1. **Use upsert semantics** - Same `creative_id` updates existing creative rather than creating duplicates. This allows iterative creative development. +1. **Use upsert semantics** - Same `creative_id` updates existing creative rather than creating duplicates. This allows iterative creative development. Note: updates are blocked for creatives in active delivery (see #7). 2. **Validate first** - Use `dry_run: true` to catch errors before actual upload. This saves bandwidth and processing time. @@ -667,6 +667,8 @@ The operation status (`completed`) means the review process finished. Individual 6. **Check format support** - Use `list_creative_formats` to verify product supports your creative formats before uploading. +7. **Active delivery protection** - Creatives assigned to active, non-paused packages cannot be updated or deleted via `delete_missing`. Pause the package first, unassign the creative via `update_media_buy`, or create a new creative with a different `creative_id`. + ## Next Steps - [list_creative_formats](/docs/media-buy/task-reference/list_creative_formats) - Check supported formats before upload diff --git a/docs/media-buy/task-reference/update_media_buy.mdx b/docs/media-buy/task-reference/update_media_buy.mdx index 237b6847e7..0dd78ea270 100644 --- a/docs/media-buy/task-reference/update_media_buy.mdx +++ b/docs/media-buy/task-reference/update_media_buy.mdx @@ -149,11 +149,25 @@ asyncio.run(create_and_pause_campaign()) | `end_time` | string | No | Updated campaign end time | | `paused` | boolean | No | Pause/resume entire media buy (`true` = paused, `false` = active) | | `packages` | PackageUpdate[] | No | Package-level updates (see below) | -| `creatives` | CreativeAsset[] | No | Upload and assign new creative assets inline | -| `creative_assignments` | CreativeAssignment[] | No | Update creative rotation weights and placement targeting | +| `reporting_webhook` | object | No | Update reporting webhook configuration (see below) | +| `push_notification_config` | object | No | Webhook for async operation notifications | *Either `media_buy_id` OR `buyer_ref` is required (not both) +### Reporting Webhook Object + +Configure automated delivery reporting for this media buy: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `url` | string | Yes | Webhook endpoint URL | +| `authentication` | object | Yes | Auth config with `schemes` and `credentials` | +| `reporting_frequency` | string | Yes | `hourly`, `daily`, or `monthly` | +| `requested_metrics` | string[] | No | Specific metrics to include (defaults to all) | +| `token` | string | No | Client token for validation (min 16 chars) | + +**Note**: `reporting_webhook` configures ongoing campaign reporting. `push_notification_config` is for async operation notifications (e.g., "notify me when this update completes"). + ### Package Update Object | Parameter | Type | Description | @@ -166,7 +180,8 @@ asyncio.run(create_and_pause_campaign()) | `pacing` | string | Updated pacing strategy | | `bid_price` | number | Updated bid price (auction products only) | | `targeting_overlay` | TargetingOverlay | Updated targeting restrictions | -| `creative_ids` | string[] | Replace assigned creatives | +| `creative_assignments` | CreativeAssignment[] | Replace assigned creatives with optional weights and placement targeting | +| `creatives` | CreativeAsset[] | Upload and assign new creatives inline (must not exist in library) | *Either `package_id` OR `buyer_ref` is required for each package update @@ -372,7 +387,7 @@ asyncio.run(update_targeting()) ### Replace Creatives -Swap out creative assets for a package: +Swap out creative assignments for a package: @@ -384,7 +399,10 @@ const result = await testAgent.updateMediaBuy({ media_buy_id: 'mb_12345', packages: [{ buyer_ref: 'ctv_package', - creative_ids: ['creative_video_v2', 'creative_display_v2'] + creative_assignments: [ + { creative_id: 'creative_video_v2' }, + { creative_id: 'creative_display_v2', weight: 60 } + ] }] }); @@ -415,7 +433,10 @@ async def replace_creatives(): packages=[ { 'buyer_ref': 'ctv_package', - 'creative_ids': ['creative_video_v2', 'creative_display_v2'] + 'creative_assignments': [ + {'creative_id': 'creative_video_v2'}, + {'creative_id': 'creative_display_v2', 'weight': 60} + ] } ] ) @@ -513,6 +534,7 @@ asyncio.run(update_multiple_packages()) βœ… **Can update:** - Start/end times (subject to seller approval) - Campaign status (active/paused) +- Reporting webhook configuration (URL, frequency, metrics) ❌ **Cannot update:** - Media buy ID @@ -548,6 +570,7 @@ Common errors and resolutions: | `BUDGET_INSUFFICIENT` | New budget below minimum | Increase budget amount | | `POLICY_VIOLATION` | Update violates content policy | Review policy requirements | | `INVALID_STATE` | Operation not allowed in current state | Check campaign status | +| `CREATIVE_ID_EXISTS` | Creative ID already exists in library | Use a different `creative_id`, assign existing creatives via `creative_assignments`, or update via `sync_creatives` | Example error response: @@ -596,13 +619,16 @@ Only specified fields are updated - omitted fields remain unchanged: } ``` -**Array replacement**: When updating arrays (like `creative_ids`), provide the complete new array: +**Array replacement**: When updating arrays (like `creative_assignments`), provide the complete new array: ```json { "packages": [{ "buyer_ref": "ctv_package", - "creative_ids": ["creative_video_v2", "creative_display_v2"] + "creative_assignments": [ + { "creative_id": "creative_video_v2" }, + { "creative_id": "creative_display_v2", "weight": 60 } + ] }] } ``` @@ -678,13 +704,14 @@ Check `affected_packages` in response to confirm changes were applied correctly. - Pending states (`working`, `submitted`) are normal, not errors - Orchestrators MUST handle pending states as part of normal workflow - `implementation_date` indicates when changes take effect (null if pending approval) +- **Inline creatives**: The `creatives` array creates NEW creatives only. To update existing creatives, use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives). To assign existing library creatives, use `creative_assignments` in the Package Update object. ## Next Steps After updating a media buy: 1. **Verify Changes**: Use [`get_media_buy_delivery`](/docs/media-buy/task-reference/get_media_buy_delivery) to confirm updates -2. **Upload New Creatives**: Use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives) if creative_ids changed +2. **Upload New Creatives**: Use [`sync_creatives`](/docs/media-buy/task-reference/sync_creatives) if creative assignments changed 3. **Monitor Performance**: Track impact of changes on campaign metrics 4. **Optimize Further**: Use [`provide_performance_feedback`](/docs/media-buy/task-reference/provide_performance_feedback) for ongoing optimization diff --git a/docs/protocol/get_adcp_capabilities.mdx b/docs/protocol/get_adcp_capabilities.mdx new file mode 100644 index 0000000000..d6f8ecb9e6 --- /dev/null +++ b/docs/protocol/get_adcp_capabilities.mdx @@ -0,0 +1,548 @@ +--- +title: get_adcp_capabilities +sidebar_position: 1 +testable: false +--- + +Discover a seller's protocol support and capabilities across all AdCP protocols. This is the first call a buyer should make to understand what a seller supports. + +**Response Time**: ~2 seconds (configuration lookup) + +**Purpose**: +- **AdCP discovery** - Does this agent support AdCP? Which versions? +- **Protocol support** - Which domain protocols (media_buy, signals)? +- **Detailed capabilities** - Features, AXE integrations, geo targeting, portfolio + +**Request Schema**: [`/schemas/v1/protocol/get-adcp-capabilities-request.json`](https://adcontextprotocol.org/schemas/v1/protocol/get-adcp-capabilities-request.json) +**Response Schema**: [`/schemas/v1/protocol/get-adcp-capabilities-response.json`](https://adcontextprotocol.org/schemas/v1/protocol/get-adcp-capabilities-response.json) + +## Tool-Based Discovery + +AdCP uses native MCP/A2A tool discovery. **The presence of `get_adcp_capabilities` in an agent's tool list indicates AdCP support.** + +``` +Discovery Flow: +1. Browse agent's tool list (MCP) or skills (A2A) +2. See 'get_adcp_capabilities' tool β†’ Agent supports AdCP +3. Call get_adcp_capabilities β†’ Get version, protocols, features, capabilities +4. Proceed based on returned capabilities +``` + +This approach: +- Uses standard MCP/A2A mechanisms (no custom extensions) +- Always returns current capabilities (not stale metadata) +- Single source of truth for all capability information + +:::note +The agent card extension (`adcp-extension.json`) is deprecated. Use tool-based discovery instead. +::: + +## Request Parameters + +| Field | Type | Description | +|-------|------|-------------| +| `protocols` | string[] | Optional. Filter to specific protocols (`media_buy`, `signals`). If omitted, returns all supported protocols. | + +## Response Structure + +### adcp + +Core AdCP protocol information: + +| Field | Type | Description | +|-------|------|-------------| +| `major_versions` | integer[] | **Required.** AdCP major versions supported (e.g., `[1]`) | + +### supported_protocols + +Array of domain protocols this seller supports: + +```json +{ + "supported_protocols": ["media_buy"] +} +``` + +### media_buy + +Media-buy protocol capabilities. Only present if `media_buy` is in `supported_protocols`. + +#### features + +Optional media-buy features. **If declared true, seller MUST honor requests using that feature.** + +| Feature | Description | +|---------|-------------| +| `inline_creative_management` | Accepts creatives inline in `create_media_buy` requests | +| `property_list_filtering` | Honors `property_list` parameter in `get_products` | +| `content_standards` | Full support for content standards configuration | + +#### execution + +Technical execution capabilities: + +| Field | Type | Description | +|-------|------|-------------| +| `axe_integrations` | string[] | Agentic ad exchange (AXE) URLs this seller can execute through | +| `creative_specs` | object | Creative specification support (VAST versions, MRAID, etc.) | +| `targeting` | object | Targeting capabilities (geo granularity) | + +##### creative_specs + +| Field | Type | Description | +|-------|------|-------------| +| `vast_versions` | string[] | VAST versions supported (e.g., `["4.0", "4.1", "4.2"]`) | +| `mraid_versions` | string[] | MRAID versions supported | +| `vpaid` | boolean | VPAID support | +| `simid` | boolean | SIMID support | + +##### targeting + +| Field | Type | Description | +|-------|------|-------------| +| `geo_countries` | boolean | Country-level targeting using ISO 3166-1 alpha-2 codes | +| `geo_regions` | boolean | Region/state-level targeting using ISO 3166-2 codes (e.g., `US-NY`, `GB-SCT`) | +| `geo_metros` | object | Metro area targeting with system-specific support | +| `geo_postal_areas` | object | Postal area targeting with country and precision support | + +**geo_metros** specifies which metro classification systems are supported: + +| System | Description | +|--------|-------------| +| `nielsen_dma` | Nielsen DMA codes (US market, e.g., `501` for NYC) | +| `uk_itl1` | UK ITL Level 1 regions | +| `uk_itl2` | UK ITL Level 2 regions | +| `eurostat_nuts2` | Eurostat NUTS Level 2 regions (EU) | + +**geo_postal_areas** specifies which postal code systems are supported: + +| System | Description | +|--------|-------------| +| `us_zip` | US 5-digit ZIP codes (e.g., `10001`) | +| `us_zip_plus_four` | US 9-digit ZIP+4 codes (e.g., `10001-1234`) | +| `gb_outward` | UK postcode district (e.g., `SW1`, `EC1`) | +| `gb_full` | UK full postcode (e.g., `SW1A 1AA`) | +| `ca_fsa` | Canadian Forward Sortation Area (e.g., `K1A`) | +| `ca_full` | Canadian full postal code (e.g., `K1A 0B1`) | +| `de_plz` | German Postleitzahl (e.g., `10115`) | +| `fr_code_postal` | French code postal (e.g., `75001`) | +| `au_postcode` | Australian postcode (e.g., `2000`) | + +#### portfolio + +Inventory portfolio information: + +| Field | Type | Description | +|-------|------|-------------| +| `publisher_domains` | string[] | **Required.** Publisher domains this seller represents | +| `primary_channels` | string[] | Main advertising channels | +| `primary_countries` | string[] | Main countries (ISO codes) | +| `description` | string | Markdown portfolio description | +| `advertising_policies` | string | Content policies and restrictions | + +### signals + +Signals protocol capabilities. Only present if `signals` is in `supported_protocols`. Reserved for future use. + +### governance + +Governance protocol capabilities. Only present if `governance` is in `supported_protocols`. Governance agents provide property data like compliance scores, brand safety ratings, and sustainability metrics. + +#### property_features + +Array of property features this governance agent can evaluate: + +| Field | Type | Description | +|-------|------|-------------| +| `feature_id` | string | **Required.** Unique identifier (e.g., `consent_quality`, `coppa_certified`) | +| `type` | string | **Required.** Data type: `binary`, `quantitative`, or `categorical` | +| `range` | object | For quantitative: `{ min, max }` | +| `categories` | string[] | For categorical: valid values | +| `description` | string | Human-readable description | +| `methodology_url` | string | URL to methodology documentation | + +**Example governance agent response:** + +```json +{ + "adcp": { "major_versions": [1] }, + "supported_protocols": ["governance"], + "governance": { + "property_features": [ + { "feature_id": "consent_quality", "type": "quantitative", "range": { "min": 0, "max": 100 }, "description": "Quality score for consent collection practices", "methodology_url": "https://vendor.example.com/methodology/consent-quality" }, + { "feature_id": "coppa_certified", "type": "binary", "description": "COPPA compliance certification" }, + { "feature_id": "carbon_score", "type": "quantitative", "range": { "min": 0, "max": 100 }, "description": "Carbon footprint sustainability score", "methodology_url": "https://scope3.com/methodology/carbon-score" }, + { "feature_id": "content_category", "type": "categorical", "categories": ["news", "entertainment", "sports", "finance"], "description": "Primary content category" } + ] + } +} +``` + +### extensions_supported + +Array of extension namespaces this agent supports. Buyers can expect meaningful data in `ext.{namespace}` fields on responses from this agent. + +| Field | Type | Description | +|-------|------|-------------| +| `extensions_supported` | string[] | Extension namespaces (e.g., `["scope3", "garm"]`) | + +Extension schemas are published in the [AdCP extension registry](/docs/reference/extensions-and-context). When an agent declares support for an extension, buyers know to look for and process `ext.{namespace}` data in responses. + +**Example:** +```json +{ + "extensions_supported": ["scope3", "garm", "iab_tcf"] +} +``` + +This tells buyers: +- Product responses may include `ext.scope3` with Scope3 sustainability data +- Creative policies may include `ext.garm` with GARM brand safety categorizations +- Responses may include `ext.iab_tcf` with IAB TCF consent data + +## The Capability Contract + +**If a capability is declared, the seller MUST honor it.** + +- `media_buy.execution.targeting.geo_postal_areas.us_zip: true` β†’ Buyer can send US ZIP codes, seller MUST target them +- `media_buy.execution.targeting.geo_metros.nielsen_dma: true` β†’ Buyer can send DMA codes, seller MUST target them +- `media_buy.features.content_standards: true` β†’ Seller MUST apply content standards when provided +- AXE URL in `media_buy.execution.axe_integrations` β†’ Seller can execute through that exchange + +No silent ignoring. If a seller can't support a capability, they should declare `false` or omit it. + +## Common Scenarios + +### Basic Capability Discovery + +```javascript +import { AdcpClient } from '@adcp/client'; + +const client = new AdcpClient({ baseUrl: 'https://seller.example.com/mcp' }); + +// Get seller capabilities +const result = await client.getAdcpCapabilities({}); + +if (result.errors) { + throw new Error(`Request failed: ${result.errors[0].message}`); +} + +// Check protocol support +console.log(`AdCP versions: ${result.adcp.major_versions.join(', ')}`); +console.log(`Supported protocols: ${result.supported_protocols.join(', ')}`); + +// Check media-buy capabilities +if (result.supported_protocols.includes('media_buy')) { + const mediaBuy = result.media_buy; + + // Check feature support + if (mediaBuy.features?.content_standards) { + console.log('Content standards supported'); + } + + // Check AXE integrations + if (mediaBuy.execution?.axe_integrations?.includes('https://agentic.scope3.com')) { + console.log('Scope3 AXE integration available'); + } + + // Check geo targeting + if (mediaBuy.execution?.targeting?.geo_postal_areas?.us_zip) { + console.log('US ZIP code targeting supported'); + } + + // Portfolio overview + console.log(`Publishers: ${mediaBuy.portfolio.publisher_domains.length}`); + console.log(`Channels: ${mediaBuy.portfolio.primary_channels?.join(', ')}`); +} +``` + +### Filter Sellers by Capability + +```javascript +// Find sellers that support specific requirements +async function findCompatibleSellers(sellers, requirements) { + const compatible = []; + + for (const sellerUrl of sellers) { + const client = new AdcpClient({ baseUrl: sellerUrl }); + const caps = await client.getAdcpCapabilities({}); + + if (caps.errors) continue; + + // Must support media_buy protocol + if (!caps.supported_protocols.includes('media_buy')) continue; + + const mediaBuy = caps.media_buy; + + // Check AXE integration requirement + if (requirements.axeIntegration) { + if (!mediaBuy.execution?.axe_integrations?.includes(requirements.axeIntegration)) { + continue; + } + } + + // Check geo targeting requirement + if (requirements.postalCodeTargeting) { + if (!mediaBuy.execution?.targeting?.geo_postal_areas?.us_zip) { + continue; + } + } + + // Check feature requirement + if (requirements.contentStandards) { + if (!mediaBuy.features?.content_standards) { + continue; + } + } + + compatible.push({ url: sellerUrl, capabilities: caps }); + } + + return compatible; +} + +// Usage +const sellers = await findCompatibleSellers( + ['https://seller1.com/mcp', 'https://seller2.com/mcp'], + { + axeIntegration: 'https://agentic.scope3.com', + postalCodeTargeting: true, + contentStandards: true + } +); +``` + +### Use Capabilities to Build Targeting + +Capabilities tell you what you CAN specify in create_media_buy targeting. Use `required_geo_targeting` to filter products to sellers that support specific geo targeting levels and systems: + +```javascript +// First, check capabilities +const caps = await client.getAdcpCapabilities({}); + +if (!caps.supported_protocols.includes('media_buy')) { + throw new Error('Seller does not support media_buy protocol'); +} + +const mediaBuy = caps.media_buy; + +// Filter products to sellers with specific geo targeting capabilities +const products = await client.getProducts({ + brief: "Premium video inventory in US for ZIP-targeted campaign", + filters: { + channels: ['olv', 'ctv'], + countries: ['US'], + // Require seller supports ZIP targeting (capability filter - no actual ZIPs needed) + // level = granularity, system = classification taxonomy + required_geo_targeting: [ + { level: 'postal_area', system: 'us_zip' } + ], + // Only include if seller supports this AXE + required_axe_integrations: ['https://agentic.scope3.com'] + } +}); + +// Then, create media buy with fine-grained targeting +// (if seller supports postal areas, we can target specific ZIP codes) +const buy = await client.createMediaBuy({ + buyer_ref: 'my-campaign-001', + brand_manifest: { url: 'https://mybrand.com' }, + packages: [{ + buyer_ref: 'package-001', + product_id: products.products[0].product_id, + pricing_option_id: products.products[0].pricing_options[0].id, + budget: 10000, + // Targeting overlay refines delivery within product coverage + targeting_overlay: { + geo_countries: ['US'], + // Only specify ZIP targeting if seller supports it + ...(mediaBuy.execution?.targeting?.geo_postal_areas?.us_zip && { + geo_postal_areas: [{ + system: 'us_zip', + values: ['10001', '10002', '10003', '10004', '10005'] + }] + }) + } + }], + start_time: { type: 'asap' }, + end_time: '2025-03-01T00:00:00Z' +}); +``` + +**Two models for product geography:** + +| Inventory Type | Filter By | Example | +|----------------|-----------|---------| +| Digital (display, OLV, CTV) | Capability: `required_geo_targeting` | Products have broad coverage, target at buy time | +| Local (radio, DOOH, local TV) | Coverage: `metros`, `regions` | Products ARE geographically bound | + +- **Digital inventory**: Use `countries` + `required_geo_targeting` (capability), apply fine-grained targeting in `create_media_buy` +- **Local inventory**: Use `metros`/`regions` (coverage) to find products with coverage in your target markets + +### Local Inventory Example (Radio, DOOH) + +For locally-bound inventory, products ARE geographically specific. A radio station in NYC DMA only covers NYC. + +```javascript +// Find radio products in specific DMAs +const radioProducts = await client.getProducts({ + brief: "Radio inventory in NYC and LA markets", + filters: { + channels: ['radio'], + // Coverage filter: products must cover these metros + metros: [ + { system: 'nielsen_dma', code: '501' }, // NYC + { system: 'nielsen_dma', code: '803' } // LA + ] + } +}); + +// For local inventory, targeting_overlay is optional - +// the product's coverage IS the geography +const buy = await client.createMediaBuy({ + buyer_ref: 'radio-campaign-001', + brand_manifest: { url: 'https://mybrand.com' }, + packages: [{ + buyer_ref: 'package-001', + product_id: radioProducts.products[0].product_id, + pricing_option_id: radioProducts.products[0].pricing_options[0].id, + budget: 5000 + // No targeting_overlay needed - product covers NYC DMA + }], + start_time: { type: 'asap' }, + end_time: '2025-03-01T00:00:00Z' +}); +``` + +## Response Example + +```json +{ + "adcp": { + "major_versions": [1] + }, + "supported_protocols": ["media_buy"], + "media_buy": { + "features": { + "inline_creative_management": true, + "property_list_filtering": true, + "content_standards": false + }, + "execution": { + "axe_integrations": ["https://agentic.scope3.com"], + "creative_specs": { + "vast_versions": ["4.0", "4.1", "4.2"], + "mraid_versions": ["3.0"], + "vpaid": false, + "simid": true + }, + "targeting": { + "geo_countries": true, + "geo_regions": true, + "geo_metros": { + "nielsen_dma": true, + "uk_itl2": false, + "eurostat_nuts2": false + }, + "geo_postal_areas": { + "us_zip": true, + "us_zip_plus_four": false, + "gb_outward": true, + "gb_full": false, + "ca_fsa": true + } + } + }, + "portfolio": { + "publisher_domains": ["example.com", "news.example.com"], + "primary_channels": ["display", "olv"], + "primary_countries": ["US", "CA"] + } + }, + "extensions_supported": ["scope3"], + "last_updated": "2025-01-23T10:00:00Z" +} +``` + +This tells buyers: +- **AdCP versions**: Version 1 +- **Protocols**: Media-buy only (signals not supported) +- **Country targeting**: Available (ISO 3166-1 alpha-2: `US`, `GB`, etc.) +- **Region targeting**: Available (ISO 3166-2: `US-NY`, `GB-SCT`, etc.) +- **Metro targeting**: Nielsen DMA only (US market) +- **Postal targeting**: US ZIP, UK outward codes, Canadian FSA +- **Extensions**: Scope3 sustainability data in `ext.scope3` + +### Geo Standards Reference + +| Level | System | Examples | +|-------|--------|----------| +| Country | ISO 3166-1 alpha-2 | `US`, `GB`, `DE`, `CA` | +| Region | ISO 3166-2 | `US-NY`, `GB-SCT`, `DE-BY`, `CA-ON` | +| Metro (US) | `nielsen_dma` | `501` (NYC), `803` (LA), `602` (Chicago) | +| Metro (UK) | `uk_itl2` | `UKI` (London), `UKD` (North West) | +| Metro (EU) | `eurostat_nuts2` | `DE30` (Berlin), `FR10` (Île-de-France) | +| Postal (US) | `us_zip` | `10001`, `90210` | +| Postal (US) | `us_zip_plus_four` | `10001-1234` | +| Postal (UK) | `gb_outward` | `SW1`, `EC1`, `M1` | +| Postal (UK) | `gb_full` | `SW1A 1AA` | +| Postal (CA) | `ca_fsa` | `K1A`, `M5V` | + +## Migration from list_authorized_properties + +If migrating from `list_authorized_properties`: + +| Old Field | New Location | +|-----------|--------------| +| `publisher_domains` | `media_buy.portfolio.publisher_domains` | +| `primary_channels` | `media_buy.portfolio.primary_channels` | +| `primary_countries` | `media_buy.portfolio.primary_countries` | +| `portfolio_description` | `media_buy.portfolio.description` | +| `advertising_policies` | `media_buy.portfolio.advertising_policies` | +| `last_updated` | `last_updated` (top level) | + +New fields: +- `adcp.major_versions` - Version compatibility +- `supported_protocols` - Which domain protocols are supported +- `media_buy.features` - Optional feature support +- `media_buy.execution.axe_integrations` - Ad exchange support +- `media_buy.execution.creative_specs` - VAST/MRAID versions +- `media_buy.execution.targeting` - Geo targeting granularity + +## Error Handling + +| Error Code | Description | Resolution | +|------------|-------------|------------| +| `AUTH_REQUIRED` | Authentication needed | Provide credentials | +| `INTERNAL_ERROR` | Server error | Retry with backoff | + +## Best Practices + +**1. Cache Capabilities** +Capabilities rarely change. Cache results and use `last_updated` for staleness detection. + +**2. Check Protocol Support First** +Before accessing protocol-specific fields, verify the protocol is in `supported_protocols`. + +**3. Check Before Requesting** +Don't send postal areas for a system the seller doesn't support. Don't request features the seller doesn't support. + +**4. Fail Fast on Incompatibility** +If a seller doesn't support required capabilities, skip them early rather than discovering failures later. + +**5. Use Protocol Version for Routing** +Route requests to appropriate API versions based on `adcp.major_versions`. + +## Next Steps + +After discovering capabilities: + +1. **Filter products**: Use [`get_products`](/docs/media-buy/task-reference/get_products) with capability-aware filters +2. **Validate properties**: Fetch publisher `adagents.json` files for property definitions +3. **Create buys**: Use [`create_media_buy`](/docs/media-buy/task-reference/create_media_buy) with supported features + +## Learn More + +- [adagents.json Specification](/docs/governance/property/adagents) - Publisher authorization files +- [Product Filters](/docs/media-buy/task-reference/get_products#filters) - Capability-aware filtering +- [Content Standards](/docs/governance/content-standards) - Brand safety configuration diff --git a/docs/protocols/a2a-guide.mdx b/docs/protocols/a2a-guide.mdx index 8bfe9b4f54..f01e2912e9 100644 --- a/docs/protocols/a2a-guide.mdx +++ b/docs/protocols/a2a-guide.mdx @@ -673,7 +673,8 @@ await a2a.send({ ``` ### Available Skills -- **Media Buy**: `get_products`, `list_creative_formats`, `create_media_buy`, `update_media_buy`, `sync_creatives`, `get_media_buy_delivery`, `list_authorized_properties`, `provide_performance_feedback` +- **Protocol**: `get_adcp_capabilities` (start here to discover agent capabilities) +- **Media Buy**: `get_products`, `list_creative_formats`, `create_media_buy`, `update_media_buy`, `sync_creatives`, `get_media_buy_delivery`, `provide_performance_feedback` - **Signals**: `get_signals`, `activate_signal` **Task Parameters**: See [Media Buy](/docs/media-buy) and [Signals](/docs/signals/overview) documentation for complete parameter specifications. @@ -711,32 +712,55 @@ console.log('Examples:', getProductsSkill.examples); ] } ], - "extensions": { - "adcp": { - "adcp_version": "2.4.0", - "protocols_supported": ["media_buy"] + "extensions": [ + { + "uri": "https://adcontextprotocol.org/extensions/adcp", + "description": "AdCP media buying protocol support", + "required": false, + "params": { + "adcp_version": "2.6.0", + "protocols_supported": ["media_buy"], + "extensions_supported": ["sustainability"] + } } - } + ] } ``` ### AdCP Extension -**Recommended**: Include the `extensions.adcp` field in your agent card to declare AdCP support programmatically. + +**Recommended**: Use [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) for runtime capability discovery. The agent card extension provides static metadata for agent registries and discovery services. + + +Include the AdCP extension in your agent card's `extensions` array to declare AdCP support programmatically. + +The A2A protocol uses an `extensions` array where each extension has: +- **`uri`**: Extension identifier (use `https://adcontextprotocol.org/extensions/adcp`) +- **`description`**: Human-readable description of how you use AdCP +- **`required`**: Whether clients must support this extension (typically `false` for AdCP) +- **`params`**: AdCP-specific configuration (see schema below) ```javascript // Check if agent supports AdCP const agentCard = await fetch('https://sales.example.com/.well-known/agent.json') .then(r => r.json()); -if (agentCard.extensions?.adcp) { - console.log('AdCP Version:', agentCard.extensions.adcp.adcp_version); - console.log('Supported domains:', agentCard.extensions.adcp.protocols_supported); +// Find the AdCP extension in the extensions array +const adcpExt = agentCard.extensions?.find( + ext => ext.uri === 'https://adcontextprotocol.org/extensions/adcp' +); + +if (adcpExt) { + console.log('AdCP Version:', adcpExt.params.adcp_version); + console.log('Supported domains:', adcpExt.params.protocols_supported); // ["media_buy", "creative", "signals"] + console.log('Typed extensions:', adcpExt.params.extensions_supported); + // ["sustainability"] } ``` -**Extension Structure**: See the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json) for complete specification. +**Extension Params Schema**: See the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json) for the complete `params` specification. **Benefits**: - Clients can discover AdCP capabilities without making test calls diff --git a/docs/protocols/core-concepts.mdx b/docs/protocols/core-concepts.mdx index dfebab3af2..dbe24b6e2b 100644 --- a/docs/protocols/core-concepts.mdx +++ b/docs/protocols/core-concepts.mdx @@ -164,7 +164,7 @@ Async operations start with `working` and provide updates: AdCP operations fall into three categories: 1. **Synchronous** - Return immediately with `completed` or `failed` - - `list_creative_formats`, `list_authorized_properties` + - `get_adcp_capabilities`, `list_creative_formats` - Fast operations that don't require external systems 2. **Interactive** - May return `input-required` before proceeding diff --git a/docs/protocols/mcp-guide.mdx b/docs/protocols/mcp-guide.mdx index c3303454dd..6a9f11d5f3 100644 --- a/docs/protocols/mcp-guide.mdx +++ b/docs/protocols/mcp-guide.mdx @@ -92,15 +92,19 @@ MCP responses use a **flat structure** where task-specific fields are at the top All AdCP tasks are available as MCP tools: +### Protocol Tools +```javascript +await mcp.call('get_adcp_capabilities', {...}); // Discover agent capabilities (start here) +``` + ### Media Buy Tools ```javascript await mcp.call('get_products', {...}); // Discover inventory await mcp.call('list_creative_formats', {...}); // Get format specs -await mcp.call('create_media_buy', {...}); // Create campaigns +await mcp.call('create_media_buy', {...}); // Create campaigns await mcp.call('update_media_buy', {...}); // Modify campaigns await mcp.call('sync_creatives', {...}); // Manage creative assets await mcp.call('get_media_buy_delivery', {...}); // Performance metrics -await mcp.call('list_authorized_properties', {...}); // Available properties await mcp.call('provide_performance_feedback', {...}); // Share outcomes ``` @@ -632,22 +636,64 @@ const adcpTools = tools.filter(t => t.name.startsWith('adcp_') || ['get_products', 'create_media_buy'].includes(t.name)); ``` -### AdCP Extension (Future) +### AdCP Extension via MCP Server Card -**Status**: MCP server cards are expected in a future MCP release. When available, AdCP servers will include the AdCP extension. + +**Recommended**: Use [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities) for runtime capability discovery. The server card extension provides static metadata for tool catalogs and registries. + + +MCP servers can declare AdCP support via a server card at `/.well-known/mcp.json` (or `/.well-known/server.json`). AdCP-specific metadata goes in the `_meta` field using the `adcontextprotocol.org` namespace. ```json { - "extensions": { - "adcp": { - "adcp_version": "2.4.0", - "protocols_supported": ["media_buy", "creative", "signals"] + "name": "io.adcontextprotocol/media-buy-agent", + "version": "1.0.0", + "title": "AdCP Media Buy Agent", + "description": "AI-powered media buying agent implementing AdCP", + "tools": [ + { "name": "get_products" }, + { "name": "create_media_buy" }, + { "name": "list_creative_formats" } + ], + "_meta": { + "adcontextprotocol.org": { + "adcp_version": "2.6.0", + "protocols_supported": ["media_buy"], + "extensions_supported": ["sustainability"] } } } ``` -This will allow clients to programmatically discover which AdCP version and protocol domains an MCP server implements. See the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json) for specification details. +**Discovering AdCP support:** + +```javascript +// Check both possible locations for MCP server card +const serverCard = await fetch('https://sales.example.com/.well-known/mcp.json') + .then(r => r.ok ? r.json() : null) + .catch(() => null) + || await fetch('https://sales.example.com/.well-known/server.json') + .then(r => r.json()); + +// Check for AdCP metadata +const adcpMeta = serverCard?._meta?.['adcontextprotocol.org']; + +if (adcpMeta) { + console.log('AdCP Version:', adcpMeta.adcp_version); + console.log('Supported domains:', adcpMeta.protocols_supported); + // ["media_buy", "creative", "signals"] + console.log('Typed extensions:', adcpMeta.extensions_supported); + // ["sustainability"] +} +``` + +**Benefits:** +- Clients can discover AdCP capabilities without making test calls +- Declare which protocol domains you implement (media_buy, creative, signals) +- Declare which [typed extensions](/docs/reference/extensions-and-context#typed-extensions) you support +- Enable compatibility checks based on version + +**Note:** The `_meta` field uses reverse DNS namespacing per the [MCP server.json spec](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/server-json/generic-server-json.md). AdCP servers should support both `/.well-known/mcp.json` and `/.well-known/server.json` locations. ### Parameter Validation ```javascript diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index ade7a90c3e..efef9f25f7 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -109,8 +109,8 @@ Now that you've seen basic product discovery, explore: ## Understanding Authentication Some operations work without credentials: +- `get_adcp_capabilities` - Discover agent capabilities - `list_creative_formats` - Browse creative formats -- `list_authorized_properties` - See publisher properties - `get_products` - Discover inventory (limited results) Most operations require authentication: diff --git a/docs/reference/authentication.mdx b/docs/reference/authentication.mdx index 0f1e4aafa4..2324b8aa58 100644 --- a/docs/reference/authentication.mdx +++ b/docs/reference/authentication.mdx @@ -12,8 +12,8 @@ AdCP uses a tiered authentication model where some operations are publicly acces These operations work without credentials to enable discovery and evaluation: +- **`get_adcp_capabilities`** - Discover agent capabilities, portfolio, and supported features - **`list_creative_formats`** - Browse available creative formats -- **`list_authorized_properties`** - See which properties an agent represents - **`get_products`** - Discover inventory (returns limited results without auth) **Rationale**: Publishers want potential buyers to discover their capabilities before establishing a business relationship. diff --git a/docs/reference/data-models.mdx b/docs/reference/data-models.mdx index 7f88e4e86a..0e66f3fc07 100644 --- a/docs/reference/data-models.mdx +++ b/docs/reference/data-models.mdx @@ -79,7 +79,7 @@ interface CreativeAsset { name: string; format: string; url?: string; - status: 'processing' | 'approved' | 'rejected' | 'pending_review'; + status: 'processing' | 'approved' | 'rejected' | 'pending_review' | 'archived'; compliance?: { status: string; issues?: string[]; @@ -166,7 +166,7 @@ type DeliveryType = 'guaranteed' | 'non_guaranteed'; type MediaBuyStatus = 'pending_activation' | 'active' | 'paused' | 'completed'; // Creative Status - Schema: /schemas/v2/enums/creative-status.json -type CreativeStatus = 'processing' | 'approved' | 'rejected' | 'pending_review'; +type CreativeStatus = 'processing' | 'approved' | 'rejected' | 'pending_review' | 'archived'; // Pacing - Schema: /schemas/v2/enums/pacing.json type Pacing = 'even' | 'asap' | 'front_loaded'; diff --git a/docs/reference/error-codes.mdx b/docs/reference/error-codes.mdx index 0132e64cdf..0eac0041c3 100644 --- a/docs/reference/error-codes.mdx +++ b/docs/reference/error-codes.mdx @@ -425,6 +425,61 @@ Request exceeded maximum processing time. **Resolution**: Refine request parameters or retry. +## Content Standards Errors + +### STANDARDS_NOT_FOUND +Specified standards ID doesn't exist. + +**Example**: +```json +{ + "$schema": "https://adcontextprotocol.org/schemas/v2/core/error.json", + "code": "STANDARDS_NOT_FOUND", + "message": "No standards found with ID 'invalid_id'", + "details": { + "standards_id": "invalid_id" + } +} +``` + +**Resolution**: Use `list_content_standards` to find valid standards IDs. + +### STANDARDS_IN_USE +Cannot delete standards that are referenced by active media buys. + +**Example**: +```json +{ + "$schema": "https://adcontextprotocol.org/schemas/v2/core/error.json", + "code": "STANDARDS_IN_USE", + "message": "Cannot delete standards 'nike_emea_safety' - currently referenced by active media buys", + "details": { + "standards_id": "nike_emea_safety", + "active_media_buy_count": 3 + } +} +``` + +**Resolution**: Wait for media buys to complete before deleting. + +### STANDARDS_SCOPE_CONFLICT +New standards configuration conflicts with existing standards for the same scope. + +**Example**: +```json +{ + "$schema": "https://adcontextprotocol.org/schemas/v2/core/error.json", + "code": "STANDARDS_SCOPE_CONFLICT", + "message": "Standards already exist for brand 'nike' in countries ['GB', 'DE']", + "details": { + "conflicting_standards_id": "nike_emea_safety", + "overlapping_countries": ["GB", "DE"] + } +} +``` + +**Resolution**: Update existing standards or narrow scope to avoid overlap. + ## Data Errors ### DATA_QUALITY_ISSUE @@ -506,7 +561,10 @@ const PERMANENT_ERRORS = [ 'INSUFFICIENT_PERMISSIONS', 'SEGMENT_NOT_FOUND', 'PLATFORM_UNAUTHORIZED', - 'UNSUPPORTED_VERSION' + 'UNSUPPORTED_VERSION', + 'STANDARDS_NOT_FOUND', + 'STANDARDS_IN_USE', + 'STANDARDS_SCOPE_CONFLICT' ]; function isRetryable(errorCode: string): boolean { diff --git a/docs/reference/extensions-and-context.mdx b/docs/reference/extensions-and-context.mdx index 48484c0201..e29ec563c3 100644 --- a/docs/reference/extensions-and-context.mdx +++ b/docs/reference/extensions-and-context.mdx @@ -181,6 +181,213 @@ Without namespacing: - Impossible to support multiple platforms simultaneously - Harder to deprecate platform-specific features +## Typed Extensions + +AdCP supports formal JSON schemas for extensions, enabling type safety and validation. Typed extensions are published in the `/schemas/extensions/` directory and can be declared in agent cards. + +### How Typed Extensions Work + +Typed extensions provide a middle ground between untyped vendor extensions and core AdCP fields: + +- **Untyped extensions** (`ext.vendor.*`) - Any JSON, no validation, vendor-specific +- **Typed extensions** (`ext.sustainability.*`) - Formal schema, validated, cross-vendor +- **Core fields** - Part of AdCP specification, required for interoperability + +Typed extensions enable SDK code generation, schema validation, and cross-vendor interoperability while remaining optional. + +### Extension Schema Registry + +The registry of available typed extensions is auto-generated from extension files: +- **Registry**: `/schemas/v1/extensions/index.json` +- **Extension schemas**: `/schemas/v1/extensions/{namespace}.json` +- **Meta schema**: `/schemas/v1/extensions/extension-meta.json` + +Each versioned schema path (`/schemas/v2.5/`, `/schemas/v2.6/`) includes only extensions valid for that version, based on the `valid_from` and `valid_until` fields in each extension. + +### Extension Versioning + +Extensions are versioned independently of the AdCP specification: + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/extensions/sustainability.json", + "title": "Sustainability Extension", + "description": "Carbon footprint and green certification data", + "valid_from": "2.5", + "valid_until": "4.0", + "docs_url": "https://adcontextprotocol.org/docs/extensions/sustainability", + "type": "object", + "properties": { + "carbon_kg_per_impression": { "type": "number" }, + "certified_green": { "type": "boolean" } + } +} +``` + +- **`valid_from`** (required): Minimum AdCP version this extension works with (e.g., `"2.5"`) +- **`valid_until`** (optional): Last AdCP version this extension works with. Omit if still valid. + +This allows extensions to be backported to earlier AdCP versions and deprecated gracefully. + +### Declaring Extension Support + +Agents declare which typed extensions they support in their discovery metadata. The location depends on the protocol: + +#### A2A Agent Cards + +A2A agents declare AdCP support in the `extensions` array at `/.well-known/agent.json`: + +```json +{ + "name": "AdCP Media Buy Agent", + "skills": [{ "name": "get_products" }, { "name": "create_media_buy" }], + "extensions": [ + { + "uri": "https://adcontextprotocol.org/extensions/adcp", + "description": "AdCP media buying protocol support", + "required": false, + "params": { + "adcp_version": "2.6.0", + "protocols_supported": ["media_buy"], + "extensions_supported": ["sustainability"] + } + } + ] +} +``` + +See the [A2A Guide](/docs/protocols/a2a-guide#adcp-extension) for complete details. + +#### MCP Server Cards + +MCP servers declare AdCP support via the `_meta` field at `/.well-known/mcp.json` or `/.well-known/server.json`: + +```json +{ + "name": "io.adcontextprotocol/media-buy-agent", + "tools": [{ "name": "get_products" }, { "name": "create_media_buy" }], + "_meta": { + "adcontextprotocol.org": { + "adcp_version": "2.6.0", + "protocols_supported": ["media_buy"], + "extensions_supported": ["sustainability"] + } + } +} +``` + +See the [MCP Guide](/docs/protocols/mcp-guide#adcp-extension-via-mcp-server-card) for complete details. + +#### Extension Params Schema + +Both protocols use the same params structure, defined in the [AdCP extension schema](https://adcontextprotocol.org/schemas/v2/protocols/adcp-extension.json): +- **`adcp_version`** (required): Semantic version of AdCP implemented (e.g., "2.6.0") +- **`protocols_supported`** (required): Array of protocol domains (media_buy, creative, signals) +- **`extensions_supported`** (optional): Array of typed extension namespaces + +When an agent declares support for an extension, clients can expect: +- The agent will accept and populate `ext.{namespace}` fields conforming to that extension's schema +- Data in `ext.{namespace}` is strongly typed and can be validated +- SDK code generation can produce type-safe interfaces for the extension + +### Using Typed Extensions + +When an agent supports a typed extension, you can include and expect data in the corresponding namespace: + +```json +{ + "product_id": "premium_ctv", + "name": "Premium CTV Package", + "ext": { + "sustainability": { + "carbon_kg_per_impression": 0.0012, + "certified_green": true + } + } +} +``` + +### Creating a Typed Extension + +To create a typed extension: + +1. **Create the extension file** at `/schemas/extensions/{namespace}.json` +2. **Follow the meta schema** defined in `/schemas/extensions/extension-meta.json` +3. **Set `valid_from`** to the minimum AdCP version your extension requires +4. **Add documentation** via the `docs_url` field + +Example extension file: + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/extensions/sustainability.json", + "title": "Sustainability Extension", + "description": "Carbon footprint and green certification data for ad products", + "valid_from": "2.5", + "docs_url": "https://adcontextprotocol.org/docs/extensions/sustainability", + "type": "object", + "properties": { + "carbon_kg_per_impression": { + "type": "number", + "description": "Estimated CO2 emissions per impression in kilograms" + }, + "certified_green": { + "type": "boolean", + "description": "Whether the inventory is certified by a green advertising program" + }, + "certification_provider": { + "type": "string", + "description": "Name of the green certification provider" + } + } +} +``` + +The build process auto-discovers extension files and includes them in the appropriate versioned schema builds. + +### Proposing Typed Extensions + +To propose a typed extension for inclusion in AdCP: + +1. **Validate in production first** - Use vendor-namespaced extensions (`ext.yourcompany.*`) to prove value +2. **Document the schema** - Define clear property names, types, and descriptions +3. **Gather adoption evidence** - Show multiple implementations or strong demand +4. **Submit an RFC** - Propose the extension with schema and use cases +5. **If accepted** - Extension is added to `/schemas/extensions/` with appropriate `valid_from` + +### Reserved Namespaces + +The following namespaces are reserved and cannot be used for typed extensions: +- `adcp`, `core`, `protocol`, `schema`, `meta`, `ext`, `context` + +These are reserved to prevent confusion with core AdCP concepts. + +### Extension Deprecation Policy + +#### When Extensions May Be Deprecated + +1. **Superseded** - A better extension exists with a documented migration path +2. **Promoted to core** - The extension becomes a core AdCP field +3. **Low adoption** - Insufficient usage across implementations +4. **Incompatible** - Breaking changes in AdCP require removal + +#### Deprecation Process + +1. Set `valid_until` to current version + 1 minor release (minimum grace period) +2. Announce deprecation in CHANGELOG +3. Publish migration guide if replacement exists +4. Add deprecation warning to extension docs +5. Extension removed from builds after `valid_until` version + +#### Emergency Deprecation + +Security issues may require immediate deprecation: +- `valid_until` set to current version +- Security advisory published +- Patch release issued + ## Proposing Spec Additions If your extension field represents **common ad tech functionality** that would benefit all AdCP implementations: diff --git a/docs/reference/implementor-faq.mdx b/docs/reference/implementor-faq.mdx index ce93b224df..0b73197b03 100644 --- a/docs/reference/implementor-faq.mdx +++ b/docs/reference/implementor-faq.mdx @@ -213,7 +213,7 @@ Best practice: Always require it. Policy checking should happen during discovery } ``` -Buyers resolve tags via `list_authorized_properties`. This keeps responses lightweight while maintaining full validation capability. +Buyers can discover the agent's portfolio via [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities), which returns the publisher domains and primary channels the agent represents. This keeps responses lightweight while maintaining full validation capability. ## Testing and Validation @@ -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/media-buy-quick-reference.mdx b/docs/reference/media-buy-quick-reference.mdx index 595bd8efca..a655efee52 100644 --- a/docs/reference/media-buy-quick-reference.mdx +++ b/docs/reference/media-buy-quick-reference.mdx @@ -10,8 +10,8 @@ Use the `call_adcp_agent` tool to execute these tasks against any AdCP sales age | Task | Purpose | Response Time | |------|---------|---------------| +| `get_adcp_capabilities` | Discover agent capabilities and portfolio | ~1s | | `get_products` | Discover inventory using natural language | ~60s | -| `list_authorized_properties` | See publisher properties | ~1s | | `list_creative_formats` | View creative specifications | ~1s | | `create_media_buy` | Create campaigns | Minutes-Days | | `update_media_buy` | Modify campaigns | Minutes-Days | @@ -21,11 +21,12 @@ Use the `call_adcp_agent` tool to execute these tasks against any AdCP sales age ## Typical Workflow -1. **Discover products**: `get_products` with a natural language brief -2. **Review formats**: `list_creative_formats` to understand creative requirements -3. **Create campaign**: `create_media_buy` with selected products and budget -4. **Upload creatives**: `sync_creatives` to add creative assets -5. **Monitor delivery**: `get_media_buy_delivery` to track performance +1. **Discover capabilities**: `get_adcp_capabilities` to understand what the agent supports +2. **Discover products**: `get_products` with a natural language brief +3. **Review formats**: `list_creative_formats` to understand creative requirements +4. **Create campaign**: `create_media_buy` with selected products and budget +5. **Upload creatives**: `sync_creatives` to add creative assets +6. **Monitor delivery**: `get_media_buy_delivery` to track performance --- @@ -42,7 +43,7 @@ Discover advertising products using natural language briefs. "url": "https://example.com" }, "filters": { - "channels": ["video", "ctv"], + "channels": ["display", "ctv"], "budget_range": { "min": 5000, "max": 50000 } } } @@ -59,9 +60,9 @@ Discover advertising products using natural language briefs. --- -### list_authorized_properties +### get_adcp_capabilities -Get the list of publisher properties this agent can sell. +Discover agent capabilities, portfolio, and supported features. **Start here** when working with a new agent. ```json {} @@ -70,7 +71,12 @@ Get the list of publisher properties this agent can sell. No parameters required. **Response contains:** -- `publisher_domains`: Array of domain strings the agent is authorized to sell +- `adcp.major_versions`: Supported AdCP versions +- `supported_protocols`: Which protocols the agent implements (media_buy, signals, governance) +- `media_buy.portfolio`: Publisher domains, primary channels, and countries +- `media_buy.execution`: Geo targeting capabilities and AXE integrations + +See [get_adcp_capabilities](/docs/protocol/get_adcp_capabilities) for complete documentation. --- diff --git a/docs/reference/media-channel-taxonomy.mdx b/docs/reference/media-channel-taxonomy.mdx new file mode 100644 index 0000000000..dbff98a547 --- /dev/null +++ b/docs/reference/media-channel-taxonomy.mdx @@ -0,0 +1,740 @@ +--- +title: Media Channel Taxonomy +sidebar_position: 15 +description: A standardized taxonomy for advertising media channels, designed for interoperability across media planning tools, ad tech platforms, and AI agents. +--- + +# Media Channel Taxonomy + + +**Status**: Draft Specification +**Version**: 1.0.0-draft +**Last Updated**: 2026-01-23 + + +This specification defines a standardized taxonomy for advertising media channels. It is designed for interoperability across media planning tools, ad tech platforms, and AI-powered advertising agents. + +## Motivation + +The advertising industry lacks a standardized channel taxonomy. While IAB Tech Lab provides taxonomies for content, audience, and ad products, no equivalent exists for media channels. This leads to: + +- Inconsistent channel naming across platforms +- Difficulty aggregating cross-channel campaign data +- Confusion between channels, formats, and buying models +- Friction in automated media planning workflows + +This specification addresses these gaps by defining: + +1. **Media Channels** - How buyers allocate budget (planning abstractions) +2. **Property Types** - Addressable inventory surfaces with verifiable ownership +3. **Clear distinction** between channels, property types, and formats + +## Design Philosophy + +**Channels represent how buyers plan and allocate budget**, not where ads technically render. + +This is a deliberate design choice. Buyers don't say "I have $500K for web" β€” they say "I have $500K for display" or "I have $300K for OLV." The channel taxonomy reflects this reality. + +### Key Principles + +1. **Planning-oriented**: Channels match how agencies structure media plans +2. **Lightweight tags**: Channels help buyers express intent, not enforce precise classification +3. **Multi-channel support**: Properties may align with multiple channels (YouTube = `olv`, `social`, `ctv`) +4. **Publisher-declared**: Publishers indicate which channels their inventory supports +5. **Agent-reconciled**: Sales agents match buyer intent with publisher claims + +### Channels vs Technical Substrates + +Channels deliberately avoid substrate-level concepts like "web" or "mobile app" because: +- Buyers don't plan that way +- Most digital inventory would fall into both categories +- The same placement can be bought different ways + +Instead, channels describe **buying contexts**: display, OLV, social, search, CTV, etc. + +## Terminology + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). + +## Definitions + +### Media Channel + +A **media channel** describes how buyers allocate budget. It represents a planning abstraction that encodes assumptions about audience, environment, and buying approach. + +Channels are defined by **buying context**, not by: +- Technical substrate (web vs app) +- How the ad looks (that's a **format**) +- The specific technology serving the ad + +### Property Type + +A **property type** describes a specific addressable inventory surface where: +- Ownership can be verified (e.g., via `adagents.json`) +- Ads can be programmatically served +- Identifiers exist for the property (domains, app IDs, device IDs) + +Property types are technical classifications, distinct from channels. The same property may support multiple channels. + +### Format Category + +A **format category** describes HOW an ad renders - its creative unit type. Examples: `video`, `audio`, `display`, `native`. Formats are orthogonal to channels. + +## Understanding Channels vs Property Types + +| Concept | Question It Answers | Determined By | Example | +|---------|---------------------|---------------|---------| +| **Channel** | How do buyers allocate budget? | Planning context | `retail_media` | +| **Property Type** | Where does the ad technically render? | Addressable inventory surface | `website` | + +### Why This Matters + +Consider retail media: when a buyer allocates budget to "retail media," they're buying: +1. Sponsored products on retailer websites +2. Display ads on retailer apps +3. Off-site ads using retailer data +4. In-store digital screens + +All of these are `retail_media` channel, but the property types vary (`website`, `mobile_app`, `dooh`). + +### Multi-Channel Properties + +Properties can align with multiple channels. Examples: + +| Property | Channels | Reasoning | +|----------|----------|-----------| +| YouTube | `olv`, `social`, `ctv` | Different buying contexts on same platform | +| ESPN App | `olv`, `display` | Supports video and display inventory | +| Amazon | `retail_media`, `search`, `display` | Multiple ad products | + +### When to Use Each + +**Use channel when:** +- Filtering products by budget allocation category +- Reporting on media mix +- Expressing buyer intent in product discovery + +**Use property_type when:** +- Validating inventory ownership via `adagents.json` +- Technical ad serving decisions +- Platform-specific targeting + +## Media Channels + +### Channel Enum + +The following 19 channels MUST be supported. Implementations MAY extend with additional channels using the `ext` field. + +| Channel | Description | +|---------|-------------| +| `display` | Digital display advertising (banners, native, rich media) | +| `olv` | Online video outside CTV (pre-roll, outstream, in-app video) | +| `social` | Social media platforms | +| `search` | Search engine advertising | +| `ctv` | Connected TV and streaming on television screens | +| `linear_tv` | Traditional broadcast and cable television | +| `radio` | Traditional AM/FM radio broadcast | +| `streaming_audio` | Digital audio streaming services | +| `podcast` | Podcast advertising | +| `dooh` | Digital out-of-home screens | +| `ooh` | Classic out-of-home (billboards, transit) | +| `print` | Newspapers, magazines, print publications | +| `cinema` | Movie theater advertising | +| `email` | Email advertising and newsletters | +| `gaming` | In-game advertising | +| `retail_media` | Retail media networks and commerce marketplaces | +| `influencer` | Creator and influencer partnerships | +| `affiliate` | Affiliate networks and performance-based partnerships | +| `product_placement` | Product placement and branded content | + +### Channel Definitions + +#### `display` + +Digital display advertising including banners, native units, and rich media across web and app environments. + +**Includes**: +- Display banners on websites +- Display ads in mobile apps +- Native content units +- Rich media ads +- Interstitials (non-video) + +**Excludes**: +- Video ads (use `olv` or `ctv`) +- Social platform ads (use `social`) +- Search results (use `search`) +- Retail media placements (use `retail_media`) + +**Typical Formats**: display, native, rich_media + +#### `olv` + +Online video advertising delivered outside of CTV/television environments. + +**Includes**: +- Pre-roll, mid-roll, post-roll on websites +- In-app video ads +- Outstream/in-feed video +- YouTube video ads (when not on TV screens) +- Video on news sites, sports sites, etc. + +**Excludes**: +- CTV/streaming on TV screens (use `ctv`) +- Social platform video (use `social`) +- Linear TV (use `linear_tv`) +- Retail media video (use `retail_media`) + +**Typical Formats**: video + +**Note**: OLV (Online Video) is a distinct planning bucket from CTV. Agencies commonly budget separately for "OLV" and "CTV" campaigns. + +#### `social` + +Social media platform advertising, regardless of technical delivery surface. + +**Includes**: +- Meta platforms (Facebook, Instagram, Threads) +- X (Twitter) +- TikTok +- LinkedIn +- Snapchat +- Pinterest +- Reddit +- YouTube (when bought through social-style targeting) + +**Excludes**: +- Influencer content on social platforms (use `influencer`) +- Video ads bought for reach, not social engagement (consider `olv`) + +**Typical Formats**: display, video, native + +**Note**: Social is defined by BUYING CONTEXT (social platform ad tools) and audience engagement model. + +#### `search` + +Search engine results pages and search advertising networks. + +**Includes**: +- Google Search ads +- Microsoft Bing ads +- DuckDuckGo ads +- App store search ads +- Shopping/product listing ads in search context + +**Excludes**: +- Display ads on search engine properties (use `display`) +- Retail media search (use `retail_media`) + +**Typical Formats**: text, display (shopping) + +#### `ctv` + +Connected TV advertising delivered through streaming applications on television screens. + +**Includes**: +- Streaming service apps (Netflix, Hulu, Max, etc.) +- Virtual MVPDs (YouTube TV, Sling, etc.) +- Free ad-supported streaming TV (FAST) +- Smart TV native apps +- Gaming console streaming apps + +**Excludes**: +- Linear broadcast/cable (use `linear_tv`) +- Video on phones/tablets/desktops (use `olv`) +- YouTube on mobile (use `olv` or `social`) + +**Typical Formats**: video + +#### `linear_tv` + +Traditional broadcast and cable television advertising. + +**Includes**: +- National broadcast networks (ABC, CBS, NBC, Fox) +- Cable networks (ESPN, CNN, HGTV) +- Local broadcast stations +- Addressable linear TV + +**Excludes**: +- Streaming on TV screens (use `ctv`) +- TV Everywhere apps on mobile (use `olv`) + +**Typical Formats**: video + +#### `radio` + +Traditional AM/FM radio broadcast advertising. + +**Includes**: +- Terrestrial radio stations +- Satellite radio (SiriusXM terrestrial simulcast) +- HD Radio + +**Excludes**: +- Streaming audio services (use `streaming_audio`) +- Podcasts (use `podcast`) + +**Typical Formats**: audio + +#### `streaming_audio` + +Digital audio streaming services. + +**Includes**: +- Music streaming (Spotify, Apple Music, Amazon Music, Pandora) +- Audio content platforms +- Digital radio streams (iHeartRadio digital, TuneIn) + +**Excludes**: +- Podcasts (use `podcast`) +- Terrestrial radio simulcast (use `radio`) + +**Typical Formats**: audio, display (companion) + +#### `podcast` + +Podcast advertising, including host-read and dynamically inserted ads. + +**Includes**: +- Host-read sponsorships +- Dynamically inserted audio ads +- Podcast network advertising + +**Excludes**: +- Music streaming (use `streaming_audio`) +- Video podcasts on YouTube (use `olv` or `social`) + +**Typical Formats**: audio + +#### `dooh` + +Digital out-of-home advertising on electronic screens in public spaces. + +**Includes**: +- Digital billboards +- Transit screens (subway, bus shelters, airports) +- Retail/mall digital displays +- Gas station screens +- Elevator screens +- Stadium/venue digital signage + +**Excludes**: +- Static billboards (use `ooh`) +- Cinema screens (use `cinema`) + +**Typical Formats**: display, video + +#### `ooh` + +Classic out-of-home advertising on physical (non-digital) surfaces. + +**Includes**: +- Static billboards +- Transit posters +- Street furniture +- Wallscapes +- Wild postings + +**Excludes**: +- Digital screens (use `dooh`) + +**Typical Formats**: display (static) + +#### `print` + +Newspaper, magazine, and other print publication advertising. + +**Includes**: +- Newspaper display ads +- Magazine display ads +- Newspaper/magazine inserts +- Trade publication advertising + +**Excludes**: +- Digital versions of publications (use `display`) +- Direct mail + +**Typical Formats**: display (static) + +#### `cinema` + +Movie theater advertising. + +**Includes**: +- Pre-show advertising +- On-screen trailers and ads +- Lobby displays +- Concession advertising + +**Excludes**: +- Streaming movie services (use `ctv`) + +**Typical Formats**: video, display + +#### `email` + +Email advertising and sponsored newsletter content. + +**Includes**: +- Sponsored email newsletters +- Email display advertising +- Dedicated email sends + +**Excludes**: +- Transactional email +- CRM/owned email marketing + +**Typical Formats**: display, native + +#### `gaming` + +In-game advertising across gaming platforms. + +**Includes**: +- In-game display ads +- Rewarded video ads +- Playable ads +- Advergames +- Esports sponsorships +- Gaming influencer integrations + +**Excludes**: +- Ads in non-gaming apps (use `display` or `olv`) +- Gaming content on streaming platforms (use `ctv` or `olv`) + +**Typical Formats**: display, video, native + +#### `retail_media` + +Retail media networks and commerce marketplace advertising. + +**Includes**: +- Retail media networks (Amazon Ads, Walmart Connect, Target Roundel) +- Grocery and delivery platforms (Instacart, DoorDash, Uber) +- Travel marketplaces (Expedia, Booking.com, Kayak) +- Financial services marketplaces +- Sponsored product listings +- On-site display and video on commerce platforms +- Off-site ads using retailer first-party data + +**Excludes**: +- General display ads (use `display`) +- Social commerce (use `social`) +- Search ads on non-commerce platforms (use `search`) + +**Typical Formats**: native, display, video + +**Note**: Retail media is distinguished by its transactional context and closed-loop attribution capabilities. + +#### `influencer` + +Creator and influencer marketing partnerships. + +**Includes**: +- Sponsored content creation +- Brand ambassador programs +- Affiliate creator partnerships +- User-generated content campaigns + +**Excludes**: +- Ads placed on creator content by platforms (use `social`) +- Podcast host reads (use `podcast`) + +**Typical Formats**: video, native, display + +**Note**: `influencer` describes the BUYING MODEL (creator partnership) rather than where content appears. + +#### `affiliate` + +Affiliate networks, comparison sites, and performance-based publisher partnerships. + +**Includes**: +- Affiliate networks (CJ, Rakuten, Impact, Awin, ShareASale) +- Comparison shopping engines (NerdWallet, Bankrate, The Points Guy) +- Review and recommendation sites +- Coupon and deal sites (RetailMeNot, Honey) +- Content commerce (editorial with affiliate links) +- Lead generation sites +- Cashback and loyalty programs + +**Excludes**: +- Standard display ads on affiliate sites (use `display`) +- Influencer partnerships (use `influencer`) +- Retail media sponsored products (use `retail_media`) + +**Typical Formats**: native, display, text + +**Note**: `affiliate` describes the BUYING MODEL (performance-based, CPA/CPC/rev-share) rather than where content appears. + +#### `product_placement` + +Product placement, branded content, and sponsorship integrations. + +**Includes**: +- Traditional product placement (film, TV) +- Virtual product placement +- Branded entertainment +- Event sponsorships +- Naming rights +- Branded content series + +**Excludes**: +- Influencer partnerships (use `influencer`) +- Standard ad placements at sponsored events (use appropriate channel) + +**Typical Formats**: native, video + +## Property Types + +Property types describe addressable inventory surfaces with verifiable ownership. They are used in `adagents.json` for authorization validation. + +### Property Type Enum + +| Property Type | Description | Example Channels | +|---------------|-------------|------------------| +| `website` | Web properties accessible via browser | `display`, `olv` | +| `mobile_app` | Native mobile applications | `display`, `olv`, `social`, `gaming` | +| `ctv_app` | Connected TV applications | `ctv` | +| `desktop_app` | Desktop applications (Electron, native) | `streaming_audio`, `gaming` | +| `dooh` | Digital out-of-home screen networks | `dooh` | +| `podcast` | Podcast feeds and episodes | `podcast` | +| `radio` | Radio station properties | `radio` | +| `streaming_audio` | Digital audio streaming properties | `streaming_audio` | + +### Channels Without Property Types + +The following channels do not have corresponding property types because they lack addressable, verifiable inventory surfaces in the traditional sense: + +- `linear_tv` - Broadcast/cable inventory is managed through different authorization mechanisms +- `ooh` - Physical inventory lacks digital identifiers +- `print` - Physical publication inventory +- `cinema` - Theater inventory management systems +- `email` - Email list ownership differs from property authorization +- `influencer` - Creator relationships rather than property ownership +- `affiliate` - Performance-based partnerships, placements appear on partner websites +- `product_placement` - Content/event relationships rather than properties +- `retail_media` - Platform-managed inventory within retail ecosystems +- `search` - Platform-managed inventory +- `social` - Platform-managed inventory + +## Relationship Between Concepts + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ MEDIA CHANNEL β”‚ +β”‚ How buyers allocate budget (planning abstraction) β”‚ +β”‚ β”‚ +β”‚ display, olv, social, search, ctv, linear_tv, podcast, β”‚ +β”‚ streaming_audio, radio, dooh, ooh, print, cinema, email, β”‚ +β”‚ gaming, retail_media, influencer, affiliate, product_placementβ”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ PROPERTY TYPE β”‚ +β”‚ Addressable inventory with verifiable ownership (technical) β”‚ +β”‚ β”‚ +β”‚ website, mobile_app, ctv_app, desktop_app, dooh, β”‚ +β”‚ podcast, radio, streaming_audio β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ FORMAT CATEGORY β”‚ +β”‚ How the ad renders (orthogonal to channel) β”‚ +β”‚ β”‚ +β”‚ audio, video, display, native, rich_media, text β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Implementation + +### JSON Schema + +Channels are defined in the AdCP schema at: + +``` +/schemas/v1/enums/channels.json +``` + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/enums/channels.json", + "title": "Media Channel", + "description": "Standardized advertising media channels describing how buyers allocate budget", + "type": "string", + "enum": [ + "display", + "olv", + "social", + "search", + "ctv", + "linear_tv", + "radio", + "streaming_audio", + "podcast", + "dooh", + "ooh", + "print", + "cinema", + "email", + "gaming", + "retail_media", + "influencer", + "affiliate", + "product_placement" + ] +} +``` + +### Usage in Product Discovery + +When filtering products by channel: + +```json +{ + "filters": { + "channels": ["ctv", "olv", "streaming_audio"] + } +} +``` + +### Usage in Property Definitions + +Properties in `adagents.json` declare which channels they support: + +```json +{ + "properties": [ + { + "property_id": "youtube_app", + "property_type": "ctv_app", + "name": "YouTube CTV", + "supported_channels": ["ctv", "olv", "social"], + "identifiers": [ + {"type": "roku_store_id", "value": "12345"} + ] + } + ] +} +``` + +### Extensibility + +Implementations MAY support additional channels beyond this specification using the `ext` field pattern: + +```json +{ + "channel": "display", + "ext": { + "sub_channel": "native_content" + } +} +``` + +## Versioning + +This taxonomy follows [Semantic Versioning](https://semver.org/): + +- **MAJOR**: Removing channels, changing channel semantics +- **MINOR**: Adding new channels (append-only) +- **PATCH**: Clarifying descriptions, fixing typos + +Adding new channels is a MINOR version change and MUST NOT break existing implementations. + +## Migration from Legacy Channel Values + +Prior to this taxonomy, AdCP used a different set of channel values. The following table maps legacy values to the new taxonomy: + +| Legacy Value | New Channel(s) | Notes | +|--------------|----------------|-------| +| `display` | `display` | No change, but now excludes video | +| `video` | `olv`, `ctv` | Split by viewing environment | +| `audio` | `streaming_audio`, `podcast`, `radio` | Split by audio type | +| `native` | `display` | Native is a format, not a channel | +| `web` | `display`, `olv` | Web is a substrate, not a planning bucket | +| `mobile_app` | `display`, `olv`, `gaming` | App is a substrate, not a planning bucket | +| `dooh` | `dooh` | No change | +| `ctv` | `ctv` | No change | +| `podcast` | `podcast` | No change | +| `retail` | `retail_media` | Renamed | +| `commerce_media` | `retail_media` | Renamed | +| `social` | `social` | No change | +| `sponsorship` | `product_placement` | Renamed and refined | + +### Migration Guidance + +1. **Identify the planning context**: Determine HOW the buyer allocates budget, not where ads technically render. + +2. **Split video by environment**: If video was a single category, split into `olv` (desktop/mobile) and `ctv` (TV screens). + +3. **Remove substrate channels**: If you had `web` or `mobile_app` as channels, map to planning-oriented channels (`display`, `olv`) based on buying context. + +4. **Update filters**: When filtering products, use the new channel values. + +## Edge Cases and Ambiguities + +### YouTube Classification + +YouTube spans multiple channels depending on context: + +| Scenario | Channel | Reasoning | +|----------|---------|-----------| +| YouTube video ad on phone/desktop | `olv` or `social` | Depends on buying approach | +| YouTube video ad on CTV | `ctv` | TV screen environment | +| YouTube Shorts | `social` | Short-form social context | +| YouTube Music | `streaming_audio` | Audio streaming context | + +### Retail Media Complexity + +Retail media may eventually warrant sub-channels: + +| Scenario | Current | Potential Future | +|----------|---------|------------------| +| Sponsored products on Amazon | `retail_media` | `retail_media_search` | +| Display on retailer site | `retail_media` | `retail_media_display` | +| Off-site using retailer data | `retail_media` | `retail_media_offsite` | + +For now, use `retail_media` with format filters to distinguish. + +### Gaming vs Display/OLV + +| Scenario | Channel | Reasoning | +|----------|---------|-----------| +| Rewarded video in mobile game via Unity Ads | `gaming` | Gaming-specific ad network | +| Banner in casual game via AdMob general pool | `display` | Standard mobile programmatic | +| Esports tournament sponsorship | `gaming` | Gaming audience context | + +### Influencer vs Social + +| Scenario | Channel | Reasoning | +|----------|---------|-----------| +| Buying promoted posts through Instagram Ads | `social` | Platform ad tools | +| Contracting an influencer directly | `influencer` | Creator partnership | +| Platform-inserted ads around creator content | `social` | Platform ad tools | + +## Future Considerations + +The following channels may be added in future versions based on market evolution: + +- `messaging` - WhatsApp Business, Telegram ads +- `xr` - VR/AR advertising +- `ai_agents` - AI assistant and chatbot advertising + +## References + +- [IAB Tech Lab Taxonomies](https://github.com/InteractiveAdvertisingBureau/Taxonomies) - Content, Audience, Ad Product +- [OpenRTB/AdCOM](https://github.com/InteractiveAdvertisingBureau/AdCOM) - Placement types and media objects +- [Planmatic Media Plan Schema](https://github.com/planmatic/mediaplanschema) - Media planning data structures +- [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) - Requirement level keywords + +## Changelog + +### 1.0.0-draft (2026-01-23) + +- Initial draft specification +- 19 channels defined (planning-oriented approach) +- 8 property types defined +- Clear distinction between channel (planning abstraction), property_type (technical surface), and format +- Multi-channel support for properties +- Migration guide from legacy values diff --git a/docs/reference/release-notes.mdx b/docs/reference/release-notes.mdx index a87e8ba344..2c9f258e3b 100644 --- a/docs/reference/release-notes.mdx +++ b/docs/reference/release-notes.mdx @@ -10,6 +10,215 @@ High-level summaries of major AdCP releases with migration guidance. For detaile --- +## Version 3.0.0-beta.1 + +**Status:** Beta | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#300-beta1) + + +**This is a beta release.** While the API is stable for testing, breaking changes may occur before the final 3.0.0 release. We encourage early adopters to test and provide feedback. + + +### What's New + +Version 3.0.0 is a **major release** introducing the Governance Protocol for brand safety and inventory curation, a complete overhaul of media channel definitions, and protocol-level capability discovery. This release establishes the foundation for enterprise-scale brand safety workflows. + +**🎯 Key Themes:** + +1. **Media Channel Taxonomy** - Complete overhaul of channel enum values to align with how buyers actually plan and allocate budgets across 19 planning-oriented media channels. + +2. **Governance Protocol** - New protocol domain for property lists, content standards, and brand safety evaluation with collaborative calibration workflows. + +3. **Protocol-Level Capability Discovery** - `get_adcp_capabilities` task replaces agent card extensions, providing runtime discovery of agent capabilities, supported protocols, and geo targeting systems. + +4. **Creative Assignment Weighting** - Replace simple creative ID arrays with weighted assignments supporting granular placement control. + +5. **Geo Targeting Systems** - Structured geographic targeting with named systems (Nielsen DMA, UK ITL, etc.) for metros and postal areas. + +### Breaking Changes + +#### Media Channel Taxonomy + +The channel enum has been completely replaced with 19 planning-oriented channels that reflect how buyers allocate budgets: + +**Before (5 channels):** +```json +["display", "video", "audio", "native", "retail"] +``` + +**After (19 channels):** +```json +[ + "display", "olv", "social", "search", "ctv", "linear_tv", + "radio", "streaming_audio", "podcast", "dooh", "ooh", + "print", "cinema", "email", "gaming", "retail_media", + "influencer", "affiliate", "product_placement" +] +``` + +See the [Media Channel Taxonomy](/docs/reference/media-channel-taxonomy) for complete definitions and migration guidance. + +#### Creative Assignments with Weighting + +Replace `creative_ids` arrays with `creative_assignments` objects supporting optional weighting and placement targeting: + +**Before:** +```json +{ + "creative_ids": ["creative_1", "creative_2"] +} +``` + +**After:** +```json +{ + "creative_assignments": [ + { "creative_id": "creative_1", "weight": 60 }, + { "creative_id": "creative_2", "weight": 40, "placement_ids": ["homepage"] } + ] +} +``` + +Simple assignments still work: +```json +{ + "creative_assignments": [ + { "creative_id": "creative_1" }, + { "creative_id": "creative_2" } + ] +} +``` + +#### Geo Targeting with Named Systems + +Metro and postal targeting now require explicit system specification: + +**Before:** +```json +{ + "targeting": { + "geo_metros": ["501", "602"], + "geo_postal_codes": ["10001", "90210"] + } +} +``` + +**After:** +```json +{ + "targeting": { + "geo_metros": [ + { "system": "nielsen_dma", "code": "501" }, + { "system": "nielsen_dma", "code": "602" } + ], + "geo_postal_areas": [ + { "system": "us_zip", "code": "10001" }, + { "system": "us_zip", "code": "90210" } + ] + } +} +``` + +**Supported metro systems:** `nielsen_dma`, `uk_itl1`, `uk_itl2`, `eurostat_nuts2` + +**Supported postal systems:** `us_zip`, `gb_outward`, `gb_full`, `ca_fsa`, `de_plz`, `fr_code_postal`, `au_postcode` + +### New Features + +#### get_adcp_capabilities Task + +Protocol-level capability discovery replacing agent card extensions: + +```json +// Request +{ + "task": "get_adcp_capabilities" +} + +// Response +{ + "adcp": { + "major_versions": [3] + }, + "supported_protocols": ["media_buy", "signals", "governance"], + "extensions_supported": ["scope3", "garm"], + "media_buy": { + "features": ["inline_creative_management", "property_list_filtering"], + "portfolio": { + "publisher_domains": ["publisher.com"], + "channels": ["display", "ctv"], + "countries": ["US", "CA"] + }, + "execution": { + "targeting": { + "geo_metros": [{ "system": "nielsen_dma" }], + "geo_postal_areas": [{ "system": "us_zip" }] + } + } + } +} +``` + +#### Governance Protocol + +New protocol domain for brand safety and inventory curation: + +**Property Lists** - Managed lists of properties for targeting/exclusion: +- `create_property_list` - Create a new property list +- `get_property_list` - Retrieve with resolved properties +- `update_property_list` - Modify filters or sources +- `delete_property_list` - Remove a property list +- `list_property_lists` - Enumerate available lists + +**Content Standards** - Brand safety evaluation: +- `create_content_standards` - Define safety/suitability policies +- `get_content_standards` - Retrieve policy configuration +- `update_content_standards` - Modify policies +- `calibrate_content` - Collaborative dialogue to align on interpretation +- `validate_content_delivery` - Batch validate delivery records + +#### Unified Asset Discovery + +Formats now include a unified `assets` array with `required` boolean: + +**Before:** +```json +{ + "assets_required": ["main_image", "headline"] +} +``` + +**After:** +```json +{ + "assets": [ + { "asset_id": "main_image", "required": true }, + { "asset_id": "headline", "required": true }, + { "asset_id": "impression_tracker", "required": false } + ] +} +``` + +This enables buyers and AI agents to discover ALL assets a format supports, including optional ones. + +### Deprecations + +| Deprecated | Replacement | Removal | +|------------|-------------|---------| +| `adcp-extension.json` agent card | `get_adcp_capabilities` task | 4.0.0 | +| `list_property_features` task | `get_adcp_capabilities` governance section | 4.0.0 | +| `assets_required` in formats | `assets` array with `required` boolean | 4.0.0 | + +### Migration Checklist + +- [ ] Update channel enum values to new taxonomy +- [ ] Replace `creative_ids` with `creative_assignments` +- [ ] Add system specification to metro/postal targeting +- [ ] Switch from agent card extension to `get_adcp_capabilities` +- [ ] Update format parsing to use `assets` array +- [ ] Test governance protocol integration if using property lists + +--- + ## Version 2.5.0 **Released:** November 2025 | [Full Changelog](https://github.com/adcontextprotocol/adcp/blob/main/CHANGELOG.md#250) @@ -219,7 +428,7 @@ New structured filters in `get_products`: "currency": "USD" }, "countries": ["US", "CA"], - "channels": ["display", "video"] + "channels": ["display", "ctv"] } } ``` diff --git a/docs/reference/roadmap.mdx b/docs/reference/roadmap.mdx index 35b1ec2d61..137ddeb29e 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/docs/reference/schema-versioning.mdx b/docs/reference/schema-versioning.mdx index 8c11d4606e..7b979d3ffd 100644 --- a/docs/reference/schema-versioning.mdx +++ b/docs/reference/schema-versioning.mdx @@ -21,7 +21,7 @@ All AdCP schemas are available at multiple paths: ### Exact Version (Recommended for Production) ``` -/schemas/2.5.0/core/product.json +/schemas/2.6.0/core/product.json ``` - **Pin to specific release** - URL never changes - **Guaranteed stability** - Schema won't update unexpectedly @@ -69,8 +69,8 @@ Bundled schemas are experimental and may change or be removed in future releases For tools that don't support `$ref` resolution (some API clients, code generators, or desktop applications), AdCP provides **bundled schemas** with all references resolved inline: ``` -/schemas/2.5.0/bundled/media-buy/create-media-buy-request.json -/schemas/2.5.0/bundled/media-buy/get-products-response.json +/schemas/2.6.0/bundled/media-buy/create-media-buy-request.json +/schemas/2.6.0/bundled/media-buy/get-products-response.json ``` ### What's Bundled @@ -91,11 +91,11 @@ Bundled schemas are generated for all request/response schemas (`*-request.json` ```javascript // Bundled - self-contained, no ref resolution needed -const schema = await fetch('https://adcp.org/schemas/2.5.0/bundled/media-buy/create-media-buy-request.json'); +const schema = await fetch('https://adcp.org/schemas/2.6.0/bundled/media-buy/create-media-buy-request.json'); const validate = ajv.compile(await schema.json()); // Modular - requires $ref resolution -const schema = await fetch('https://adcp.org/schemas/2.5.0/media-buy/create-media-buy-request.json'); +const schema = await fetch('https://adcp.org/schemas/2.6.0/media-buy/create-media-buy-request.json'); // This schema contains $ref to other schemas that must be resolved ``` @@ -105,7 +105,7 @@ Bundled schemas include a `_bundled` field with generation metadata: ```json { - "$id": "/schemas/2.5.0/bundled/core/product.json", + "$id": "/schemas/2.6.0/bundled/core/product.json", "title": "Product", "_bundled": { "generatedAt": "2024-12-17T10:30:00.000Z", @@ -120,7 +120,7 @@ Bundled schemas include a `_bundled` field with generation metadata: ```javascript // Production - pin exact version -const SCHEMA_VERSION = '2.5.0'; +const SCHEMA_VERSION = '2.6.0'; const schema = await fetch(`https://adcp.org/schemas/${SCHEMA_VERSION}/core/product.json`); // Stable development - track minor version (patch updates only) @@ -137,12 +137,12 @@ Always use exact versions to ensure generated types match your target protocol v ```bash # TypeScript SDK generation npx json-schema-to-typescript \ - https://adcp.org/schemas/2.5.0/core/product.json \ + https://adcp.org/schemas/2.6.0/core/product.json \ --output types/product.d.ts # Python SDK generation datamodel-codegen \ - --url https://adcp.org/schemas/2.5.0/core/product.json \ + --url https://adcp.org/schemas/2.6.0/core/product.json \ --output models/product.py ``` @@ -153,7 +153,7 @@ datamodel-codegen \ import Ajv from 'ajv'; const ajv = new Ajv(); -const schema = await fetch('https://adcp.org/schemas/2.5.0/core/product.json'); +const schema = await fetch('https://adcp.org/schemas/2.6.0/core/product.json'); const validate = ajv.compile(await schema.json()); if (!validate(data)) { @@ -227,7 +227,7 @@ If you're currently using `/schemas/v2/`: ``` 3. **Consider pinning** for production stability: ```javascript - const schema = await fetch('/schemas/2.5.0/core/product.json'); + const schema = await fetch('/schemas/2.6.0/core/product.json'); ``` ### From Exact Version to Major Alias @@ -270,11 +270,11 @@ If you want to track the latest 2.x: ```bash # Get schema registry with version info curl https://adcp.org/schemas/v2/index.json | jq '.adcp_version' -# Output: "2.5.0" +# Output: "2.6.0" # Get version from specific release -curl https://adcp.org/schemas/2.5.0/index.json | jq '.adcp_version' -# Output: "2.5.0" +curl https://adcp.org/schemas/2.6.0/index.json | jq '.adcp_version' +# Output: "2.6.0" ``` ### List Available Versions diff --git a/docs/reference/security.mdx b/docs/reference/security.mdx index 74b01e63de..1f138708ed 100644 --- a/docs/reference/security.mdx +++ b/docs/reference/security.mdx @@ -119,9 +119,9 @@ These operations are publicly accessible to enable discovery: | Operation | Risk | Auth Required | |-----------|------|---------------| +| `get_adcp_capabilities` | Agent capability discovery | None | | `get_products` | Public inventory discovery | Optional (limited results without auth) | | `list_creative_formats` | Public format catalog | None | -| `list_authorized_properties` | Public property listing | None | ## Authentication & Authorization @@ -167,7 +167,7 @@ AdCP implementations should implement fine-grained authorization: | Scope | Description | Example Operations | |-------|-------------|-------------------| -| `read:products` | Discover available inventory | `get_products`, `list_creative_formats`, `list_authorized_properties` | +| `read:products` | Discover available inventory | `get_adcp_capabilities`, `get_products`, `list_creative_formats` | | `read:campaigns` | View campaign details | `list_creatives`, `get_media_buy_delivery` | | `write:campaigns` | Create and modify campaigns | `create_media_buy`, `update_media_buy` | | `read:creatives` | Preview and inspect creatives | `preview_creative` | @@ -960,9 +960,9 @@ Implement multi-tiered rate limiting to prevent abuse while allowing legitimate ```typescript const RATE_LIMITS = { // Discovery tasks (read-only, cacheable) + 'get_adcp_capabilities': { requests: 100, window: '1m' }, 'get_products': { requests: 100, window: '1m' }, 'list_creative_formats': { requests: 100, window: '1m' }, - 'list_authorized_properties': { requests: 100, window: '1m' }, // Campaign management (state-changing, expensive) 'create_media_buy': { requests: 10, window: '1m' }, diff --git a/docs/reference/signals-quick-reference.mdx b/docs/reference/signals-quick-reference.mdx index 084b7bd974..a3f068c81b 100644 --- a/docs/reference/signals-quick-reference.mdx +++ b/docs/reference/signals-quick-reference.mdx @@ -53,7 +53,7 @@ Discover signals based on natural language description, with deployment status a - `signal_spec` (string, required): Natural language description of desired signals - `deliver_to` (object, required): Where signals will be used - `deployments` (array): Target platforms/agents with `type`, `platform`/`agent_url`, and optional `account` - - `countries` (array): ISO country codes where signals will be used + - `countries` (array): ISO 3166-1 alpha-2 country codes where signals will be used (case-insensitive, uppercase recommended) - `filters` (object, optional): Filter by `catalog_types`, `data_providers`, `max_cpm`, `min_coverage_percentage` - `max_results` (number, optional): Limit number of results diff --git a/docs/signals/tasks/get_signals.mdx b/docs/signals/tasks/get_signals.mdx index 74d9e8be85..972f8571de 100644 --- a/docs/signals/tasks/get_signals.mdx +++ b/docs/signals/tasks/get_signals.mdx @@ -27,7 +27,7 @@ The `get_signals` task returns both signal metadata and real-time deployment sta | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `deployments` | Destination[] | Yes | List of deployment targets (DSPs, sales agents, etc.) - see Destination Object below | -| `countries` | string[] | Yes | Countries where signals will be used (ISO codes) | +| `countries` | string[] | Yes | Countries where signals will be used (ISO 3166-1 alpha-2 codes, case-insensitive, uppercase recommended) | ### Destination Object diff --git a/package-lock.json b/package-lock.json index c38f776b29..318a7405d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "adcontextprotocol", - "version": "2.5.3", + "version": "2.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "adcontextprotocol", - "version": "2.5.3", + "version": "2.6.0", "dependencies": { "@adcp/client": "^3.5.2", "@anthropic-ai/sdk": "^0.71.2", diff --git a/package.json b/package.json index 8cb9c976a1..ccde0040ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "adcontextprotocol", - "version": "2.5.3", + "version": "3.0.0-beta.1", "private": true, "type": "module", "scripts": { @@ -18,6 +18,7 @@ "test:schemas": "node tests/schema-validation.test.cjs", "test:examples": "node tests/example-validation-simple.test.cjs", "test:extensions": "node tests/extension-fields.test.cjs", + "test:extension-schemas": "node tests/extension-schemas.test.cjs", "test:snippets": "node tests/snippet-validation.test.cjs", "test:json-schema": "node tests/json-schema-validation.test.cjs", "test:error-handling": "node tests/check-error-handling.cjs", @@ -27,7 +28,7 @@ "test:registry": "vitest run server/tests", "test:docker": "docker compose -f docker-compose.test.yml up --build --abort-on-container-exit", "generate-openapi": "node scripts/generate-openapi.cjs", - "test": "npm run test:schemas && npm run test:examples && npm run test:extensions && npm run test:error-handling && npm run test:json-schema && npm run test:composed && npm run test:migrations && npm run test:unit && npm run typecheck", + "test": "npm run test:schemas && npm run test:examples && npm run test:extensions && npm run test:extension-schemas && npm run test:error-handling && npm run test:json-schema && npm run test:composed && npm run test:migrations && npm run test:unit && npm run typecheck", "test:all": "npm run test:schemas && npm run test:examples && npm run test:extensions && npm run test:error-handling && npm run test:snippets && npm run typecheck", "precommit": "npm test", "prepare": "husky", diff --git a/scripts/build-schemas.cjs b/scripts/build-schemas.cjs index f171c2a76e..e85daf48c4 100644 --- a/scripts/build-schemas.cjs +++ b/scripts/build-schemas.cjs @@ -21,6 +21,11 @@ * - /schemas/v{major}/ - Points to latest release of that major version * - /schemas/v{major}.{minor}/ - Points to latest release of that minor version * - /schemas/v1/ - Backward compatibility (always points to latest/) + * + * Extension handling: + * - Extensions are auto-discovered from static/schemas/source/extensions/ + * - Each extension has valid_from/valid_until to specify compatible AdCP versions + * - The build generates extensions/index.json with extensions valid for the target version */ const fs = require('fs'); @@ -110,6 +115,228 @@ function ensureDir(dir) { } } +/** + * Compare two minor versions (e.g., "2.5" vs "2.6") + * Returns: negative if a < b, 0 if equal, positive if a > b + */ +function compareMinorVersions(a, b) { + const [aMajor, aMinor] = a.split('.').map(Number); + const [bMajor, bMinor] = b.split('.').map(Number); + if (aMajor !== bMajor) return aMajor - bMajor; + return aMinor - bMinor; +} + +/** + * Reserved namespaces that cannot be used for typed extensions + * These could cause confusion with core AdCP concepts + */ +const RESERVED_NAMESPACES = ['adcp', 'core', 'protocol', 'schema', 'meta', 'ext', 'context']; + +/** + * Validate that an extension namespace is not reserved + * @param {string} namespace - Extension namespace to validate + * @throws {Error} If namespace is reserved + */ +function validateExtensionNamespace(namespace) { + if (RESERVED_NAMESPACES.includes(namespace.toLowerCase())) { + throw new Error(`Namespace "${namespace}" is reserved and cannot be used for extensions`); + } +} + +/** + * Discover extension files from the extensions directory + * Returns array of { namespace, schema, path } objects + */ +function discoverExtensions(extensionsDir) { + if (!fs.existsSync(extensionsDir)) { + return []; + } + + const extensions = []; + const files = fs.readdirSync(extensionsDir); + + for (const file of files) { + // Skip non-JSON files and special files + if (!file.endsWith('.json')) continue; + if (file === 'index.json' || file === 'extension-meta.json') continue; + + const filePath = path.join(extensionsDir, file); + try { + const content = JSON.parse(fs.readFileSync(filePath, 'utf8')); + + // Extract namespace from $id (e.g., /schemas/extensions/sustainability.json -> sustainability) + const namespace = file.replace('.json', ''); + + // Validate namespace is not reserved + validateExtensionNamespace(namespace); + + extensions.push({ + namespace, + schema: content, + path: filePath + }); + } catch (error) { + console.warn(` ⚠️ Failed to parse extension ${file}: ${error.message}`); + } + } + + return extensions; +} + +/** + * Filter extensions to those valid for a given AdCP version + * @param {Array} extensions - Array of extension objects from discoverExtensions + * @param {string} targetVersion - Target AdCP version (e.g., "2.5.0" or "2.5") + * @returns {Array} Extensions valid for the target version + */ +function filterExtensionsForVersion(extensions, targetVersion) { + // Normalize to minor version for comparison + const targetMinor = getMinorVersion(targetVersion); + + return extensions.filter(ext => { + const { valid_from, valid_until } = ext.schema; + + // Must have valid_from + if (!valid_from) { + console.warn(` ⚠️ Extension ${ext.namespace} missing valid_from, skipping`); + return false; + } + + // Check valid_from <= targetVersion + if (compareMinorVersions(valid_from, targetMinor) > 0) { + return false; // Extension requires newer version + } + + // Check valid_until >= targetVersion (if specified) + if (valid_until && compareMinorVersions(valid_until, targetMinor) < 0) { + return false; // Extension no longer valid for this version + } + + return true; + }); +} + +/** + * Generate the extensions/index.json registry for a target version + * @param {Array} extensions - Array of valid extension objects + * @param {string} targetVersion - Target version string for $id paths + * @returns {Object} The generated registry object + */ +function generateExtensionRegistry(extensions, targetVersion) { + const registry = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: `/schemas/${targetVersion}/extensions/index.json`, + title: 'AdCP Extension Registry', + description: 'Auto-generated registry of formal AdCP extensions. Extensions provide typed schemas for vendor-specific or domain-specific data within the ext field. Agents declare which extensions they support in their agent card.', + _generated: true, + _generatedAt: new Date().toISOString(), + extensions: {} + }; + + for (const ext of extensions) { + registry.extensions[ext.namespace] = { + $ref: `/schemas/${targetVersion}/extensions/${ext.namespace}.json`, + title: ext.schema.title, + description: ext.schema.description, + valid_from: ext.schema.valid_from + }; + + // Include valid_until if specified + if (ext.schema.valid_until) { + registry.extensions[ext.namespace].valid_until = ext.schema.valid_until; + } + + // Include docs_url if specified + if (ext.schema.docs_url) { + registry.extensions[ext.namespace].docs_url = ext.schema.docs_url; + } + } + + return registry; +} + +/** + * Build extensions for a target directory + * - Discovers all extensions from source + * - Filters to those valid for target version + * - Copies valid extension schemas + * - Generates the index.json registry + */ +function buildExtensions(sourceDir, targetDir, version) { + const sourceExtensionsDir = path.join(sourceDir, 'extensions'); + const targetExtensionsDir = path.join(targetDir, 'extensions'); + + // Always ensure extensions directory exists + ensureDir(targetExtensionsDir); + + // Discover all extensions + const allExtensions = discoverExtensions(sourceExtensionsDir); + + if (allExtensions.length === 0) { + // No extensions yet - just copy the meta schema and generate empty registry + const metaSchemaPath = path.join(sourceExtensionsDir, 'extension-meta.json'); + if (fs.existsSync(metaSchemaPath)) { + let content = fs.readFileSync(metaSchemaPath, 'utf8'); + // Update $id to include version + content = content.replace( + /"\$id":\s*"\/schemas\//g, + `"$id": "/schemas/${version}/` + ); + fs.writeFileSync(path.join(targetExtensionsDir, 'extension-meta.json'), content); + } + + // Generate empty registry + const registry = generateExtensionRegistry([], version); + fs.writeFileSync( + path.join(targetExtensionsDir, 'index.json'), + JSON.stringify(registry, null, 2) + ); + + return { total: 0, included: 0 }; + } + + // Filter extensions valid for this version + const validExtensions = filterExtensionsForVersion(allExtensions, version); + + // Copy extension-meta.json (with version transform) + const metaSchemaPath = path.join(sourceExtensionsDir, 'extension-meta.json'); + if (fs.existsSync(metaSchemaPath)) { + let content = fs.readFileSync(metaSchemaPath, 'utf8'); + content = content.replace( + /"\$id":\s*"\/schemas\//g, + `"$id": "/schemas/${version}/` + ); + fs.writeFileSync(path.join(targetExtensionsDir, 'extension-meta.json'), content); + } + + // Copy each valid extension schema (with version transform) + for (const ext of validExtensions) { + let content = JSON.stringify(ext.schema, null, 2); + // Update $id to include version + content = content.replace( + /"\$id":\s*"\/schemas\//g, + `"$id": "/schemas/${version}/` + ); + fs.writeFileSync( + path.join(targetExtensionsDir, `${ext.namespace}.json`), + content + ); + } + + // Generate the registry index + const registry = generateExtensionRegistry(validExtensions, version); + fs.writeFileSync( + path.join(targetExtensionsDir, 'index.json'), + JSON.stringify(registry, null, 2) + ); + + return { + total: allExtensions.length, + included: validExtensions.length, + extensions: validExtensions.map(e => e.namespace) + }; +} + function copyAndTransformSchemas(sourceDir, targetDir, version) { const entries = fs.readdirSync(sourceDir, { withFileTypes: true }); @@ -118,6 +345,10 @@ function copyAndTransformSchemas(sourceDir, targetDir, version) { const targetPath = path.join(targetDir, entry.name); if (entry.isDirectory()) { + // Skip extensions directory - handled separately by buildExtensions() + if (entry.name === 'extensions') { + continue; + } ensureDir(targetPath); copyAndTransformSchemas(sourcePath, targetPath, version); } else if (entry.name.endsWith('.json')) { @@ -406,6 +637,15 @@ async function main() { ensureDir(versionDir); copyAndTransformSchemas(SOURCE_DIR, versionDir, version); + // Build extensions (auto-discovered, filtered by version) + console.log(`πŸ”Œ Building extensions for ${version}`); + const extResult = buildExtensions(SOURCE_DIR, versionDir, version); + if (extResult.total === 0) { + console.log(` βœ“ No extensions defined yet (empty registry created)`); + } else { + console.log(` βœ“ Included ${extResult.included}/${extResult.total} extensions: ${extResult.extensions.join(', ') || 'none'}`); + } + // Generate bundled schemas for release const bundledDir = path.join(versionDir, 'bundled'); console.log(`πŸ“¦ Generating bundled schemas to dist/schemas/${version}/bundled/`); @@ -424,6 +664,9 @@ async function main() { ensureDir(latestDir); copyAndTransformSchemas(SOURCE_DIR, latestDir, 'latest'); + // Build extensions for latest (using full version for filtering) + buildExtensions(SOURCE_DIR, latestDir, version); + // Generate bundled schemas for latest const latestBundledDir = path.join(latestDir, 'bundled'); await generateBundledSchemas(SOURCE_DIR, latestBundledDir, 'latest'); @@ -472,6 +715,15 @@ async function main() { ensureDir(latestDir); copyAndTransformSchemas(SOURCE_DIR, latestDir, 'latest'); + // Build extensions (auto-discovered, filtered by current version) + console.log(`πŸ”Œ Building extensions for ${version}`); + const extResult = buildExtensions(SOURCE_DIR, latestDir, version); + if (extResult.total === 0) { + console.log(` βœ“ No extensions defined yet (empty registry created)`); + } else { + console.log(` βœ“ Included ${extResult.included}/${extResult.total} extensions: ${extResult.extensions.join(', ') || 'none'}`); + } + // Generate bundled schemas for latest const bundledDir = path.join(latestDir, 'bundled'); console.log(`πŸ“¦ Generating bundled schemas to dist/schemas/latest/bundled/`); diff --git a/scripts/sync-versioned-docs.sh b/scripts/sync-versioned-docs.sh deleted file mode 100755 index 00737ec6d2..0000000000 --- a/scripts/sync-versioned-docs.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash -# -# Sync v2.6-rc docs from 2.6.x branch for local testing -# -# Usage: -# ./scripts/sync-versioned-docs.sh # Sync from remote 2.6.x branch -# ./scripts/sync-versioned-docs.sh --local # Sync from local 2.6.x branch (if checked out) -# - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ROOT_DIR="$(dirname "$SCRIPT_DIR")" - -cd "$ROOT_DIR" - -echo "πŸ”„ Syncing v2.6-rc docs from 2.6.x branch..." - -# Check if --local flag is passed -if [[ "$1" == "--local" ]]; then - # Check if 2.6.x branch exists locally - if ! git show-ref --verify --quiet refs/heads/2.6.x; then - echo "❌ Local branch 2.6.x not found. Fetch it first or use remote sync." - exit 1 - fi - SOURCE_REF="2.6.x" - echo "πŸ“‚ Using local 2.6.x branch" -else - # Fetch latest from remote - echo "πŸ“‘ Fetching latest 2.6.x from remote..." - git fetch origin 2.6.x - SOURCE_REF="origin/2.6.x" - echo "πŸ“‚ Using remote origin/2.6.x branch" -fi - -# Remove existing v2.6-rc/docs -rm -rf v2.6-rc/docs -mkdir -p v2.6-rc - -# Extract docs from 2.6.x branch -echo "πŸ“¦ Extracting docs folder..." -git archive "$SOURCE_REF" -- docs | tar -x -C v2.6-rc/ - -# Count files synced -FILE_COUNT=$(find v2.6-rc/docs -type f | wc -l | tr -d ' ') - -echo "" -echo "βœ… Synced $FILE_COUNT files to v2.6-rc/docs/" -echo "" -echo "To test locally with Mintlify:" -echo " npx mintlify dev" -echo "" -echo "⚠️ Note: v2.6-rc/ is tracked in git. Don't commit local changes" -echo " unless you want to override the automated sync." - diff --git a/server/src/http.ts b/server/src/http.ts index 1e5dc64374..a0a0678da2 100644 --- a/server/src/http.ts +++ b/server/src/http.ts @@ -5756,18 +5756,23 @@ Disallow: /api/admin/ return res.json({ success: true, - formats: formats.map(format => ({ - format_id: format.format_id, - name: format.name, - type: format.type, - description: format.description, - preview_image: format.preview_image, - example_url: format.example_url, - renders: format.renders, - assets_required: format.assets_required, - output_format_ids: format.output_format_ids, - agent_url: format.agent_url, - })), + formats: formats.map(format => { + // Cast to allow 'assets' field (added in schema v2.5.2, @adcp/client may not have it yet) + const formatWithAssets = format as typeof format & { assets?: unknown }; + return { + format_id: format.format_id, + name: format.name, + type: format.type, + description: format.description, + preview_image: format.preview_image, + example_url: format.example_url, + renders: format.renders, + assets_required: format.assets_required, // deprecated but kept for backward compatibility + assets: formatWithAssets.assets, // new unified field + output_format_ids: format.output_format_ids, + agent_url: format.agent_url, + }; + }), }); } catch (error) { logger.error({ err: error, url }, 'Agent formats fetch error'); diff --git a/static/schemas/source/adagents.json b/static/schemas/source/adagents.json index 286aee996f..9a45c1ff3c 100644 --- a/static/schemas/source/adagents.json +++ b/static/schemas/source/adagents.json @@ -70,6 +70,11 @@ "description": "TAG Certified Against Fraud ID for verification (if applicable)", "minLength": 1, "maxLength": 100 + }, + "privacy_policy_url": { + "type": "string", + "format": "uri", + "description": "URL to the entity's privacy policy. Used for consumer consent flows when interacting with this sales agent." } }, "required": [ @@ -264,6 +269,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": [ @@ -318,7 +355,8 @@ "email": "adops@meta.com", "domain": "meta.com", "seller_id": "pub-meta-12345", - "tag_id": "12345" + "tag_id": "12345", + "privacy_policy_url": "https://www.meta.com/privacy/policy" }, "properties": [ { @@ -489,6 +527,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/content-standards/artifact-webhook-payload.json b/static/schemas/source/content-standards/artifact-webhook-payload.json new file mode 100644 index 0000000000..63132ff516 --- /dev/null +++ b/static/schemas/source/content-standards/artifact-webhook-payload.json @@ -0,0 +1,72 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/artifact-webhook-payload.json", + "title": "Artifact Webhook Payload", + "description": "Payload sent by sales agents to orchestrators when pushing content artifacts for governance validation. Complements get_media_buy_artifacts for push-based artifact delivery.", + "type": "object", + "properties": { + "media_buy_id": { + "type": "string", + "description": "Media buy identifier these artifacts belong to" + }, + "batch_id": { + "type": "string", + "description": "Unique identifier for this batch of artifacts. Use for deduplication and acknowledgment." + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When this batch was generated (ISO 8601)" + }, + "artifacts": { + "type": "array", + "description": "Content artifacts from delivered impressions", + "items": { + "type": "object", + "properties": { + "artifact": { + "$ref": "/schemas/content-standards/artifact.json", + "description": "The content artifact" + }, + "delivered_at": { + "type": "string", + "format": "date-time", + "description": "When the impression was delivered (ISO 8601)" + }, + "impression_id": { + "type": "string", + "description": "Optional impression identifier for correlation with delivery reports" + }, + "package_id": { + "type": "string", + "description": "Package within the media buy this artifact relates to" + } + }, + "required": ["artifact", "delivered_at"] + } + }, + "pagination": { + "type": "object", + "description": "Pagination info when batching large artifact sets", + "properties": { + "total_artifacts": { + "type": "integer", + "description": "Total artifacts in the delivery period" + }, + "batch_number": { + "type": "integer", + "description": "Current batch number (1-indexed)" + }, + "total_batches": { + "type": "integer", + "description": "Total batches for this delivery period" + } + } + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["media_buy_id", "batch_id", "timestamp", "artifacts"], + "additionalProperties": true +} diff --git a/static/schemas/source/content-standards/artifact.json b/static/schemas/source/content-standards/artifact.json new file mode 100644 index 0000000000..fda9f2af12 --- /dev/null +++ b/static/schemas/source/content-standards/artifact.json @@ -0,0 +1,309 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/artifact.json", + "title": "Artifact", + "description": "Content artifact for safety and suitability evaluation. An artifact represents content adjacent to an ad placement - a news article, podcast segment, video chapter, or social post. Artifacts are collections of assets (text, images, video, audio) plus metadata and signals.", + "type": "object", + "properties": { + "property_id": { + "type": "object", + "description": "Identifier for the property where this artifact appears", + "properties": { + "type": { + "$ref": "/schemas/enums/identifier-types.json", + "description": "Type of property identifier" + }, + "value": { + "type": "string", + "description": "The identifier value" + } + }, + "required": ["type", "value"], + "additionalProperties": false + }, + "artifact_id": { + "type": "string", + "description": "Identifier for this artifact within the property. The property owner defines the scheme (e.g., 'article_12345', 'episode_42_segment_3', 'post_abc123')." + }, + "variant_id": { + "type": "string", + "description": "Identifies a specific variant of this artifact. Use for A/B tests, translations, or temporal versions. Examples: 'en', 'es-MX', 'v2', 'headline_test_b'. The combination of artifact_id + variant_id must be unique." + }, + "format_id": { + "type": "object", + "description": "Optional reference to a format definition. Uses the same format registry as creative formats.", + "properties": { + "agent_url": { + "type": "string", + "format": "uri", + "description": "Base URL of the agent that defines this format" + }, + "id": { + "type": "string", + "description": "Format identifier within that agent's registry" + } + }, + "required": ["agent_url", "id"], + "additionalProperties": false + }, + "url": { + "type": "string", + "format": "uri", + "description": "Optional URL for this artifact (web page, podcast feed, video page). Not all artifacts have URLs (e.g., Instagram content, podcast segments, TV scenes)." + }, + "published_time": { + "type": "string", + "format": "date-time", + "description": "When the artifact was published (ISO 8601 format)" + }, + "last_update_time": { + "type": "string", + "format": "date-time", + "description": "When the artifact was last modified (ISO 8601 format)" + }, + "assets": { + "type": "array", + "description": "Artifact assets in document flow order - text blocks, images, video, audio", + "items": { + "oneOf": [ + { + "type": "object", + "description": "Text block (paragraph, heading, etc.)", + "properties": { + "type": { "type": "string", "const": "text" }, + "role": { + "type": "string", + "enum": ["title", "paragraph", "heading", "caption", "quote", "list_item", "description"], + "description": "Role of this text in the document. Use 'title' for the main artifact title, 'description' for summaries." + }, + "content": { + "type": "string", + "description": "Text content" + }, + "language": { + "type": "string", + "description": "BCP 47 language tag for this text (e.g., 'en', 'es-MX'). Useful when artifact contains mixed-language content." + }, + "heading_level": { + "type": "integer", + "minimum": 1, + "maximum": 6, + "description": "Heading level (1-6), only for role=heading" + } + }, + "required": ["type", "content"] + }, + { + "type": "object", + "description": "Image asset", + "properties": { + "type": { "type": "string", "const": "image" }, + "url": { + "type": "string", + "format": "uri", + "description": "Image URL" + }, + "access": { + "$ref": "#/$defs/asset_access", + "description": "Authentication for secured URLs" + }, + "alt_text": { + "type": "string", + "description": "Alt text or image description" + }, + "caption": { + "type": "string", + "description": "Image caption" + }, + "width": { + "type": "integer", + "description": "Image width in pixels" + }, + "height": { + "type": "integer", + "description": "Image height in pixels" + } + }, + "required": ["type", "url"] + }, + { + "type": "object", + "description": "Video asset", + "properties": { + "type": { "type": "string", "const": "video" }, + "url": { + "type": "string", + "format": "uri", + "description": "Video URL" + }, + "access": { + "$ref": "#/$defs/asset_access", + "description": "Authentication for secured URLs" + }, + "duration_ms": { + "type": "integer", + "description": "Video duration in milliseconds" + }, + "transcript": { + "type": "string", + "description": "Video transcript" + }, + "transcript_source": { + "type": "string", + "enum": ["original_script", "subtitles", "closed_captions", "dub", "generated"], + "description": "How the transcript was generated" + }, + "thumbnail_url": { + "type": "string", + "format": "uri", + "description": "Video thumbnail URL" + } + }, + "required": ["type", "url"] + }, + { + "type": "object", + "description": "Audio asset", + "properties": { + "type": { "type": "string", "const": "audio" }, + "url": { + "type": "string", + "format": "uri", + "description": "Audio URL" + }, + "access": { + "$ref": "#/$defs/asset_access", + "description": "Authentication for secured URLs" + }, + "duration_ms": { + "type": "integer", + "description": "Audio duration in milliseconds" + }, + "transcript": { + "type": "string", + "description": "Audio transcript" + }, + "transcript_source": { + "type": "string", + "enum": ["original_script", "closed_captions", "generated"], + "description": "How the transcript was generated" + } + }, + "required": ["type", "url"] + } + ] + } + }, + "metadata": { + "type": "object", + "description": "Rich metadata extracted from the artifact", + "properties": { + "canonical": { + "type": "string", + "format": "uri", + "description": "Canonical URL" + }, + "author": { + "type": "string", + "description": "Artifact author name" + }, + "keywords": { + "type": "string", + "description": "Artifact keywords" + }, + "open_graph": { + "type": "object", + "description": "Open Graph protocol metadata", + "additionalProperties": true + }, + "twitter_card": { + "type": "object", + "description": "Twitter Card metadata", + "additionalProperties": true + }, + "json_ld": { + "type": "array", + "description": "JSON-LD structured data (schema.org)", + "items": { "type": "object" } + } + }, + "additionalProperties": true + }, + "identifiers": { + "type": "object", + "description": "Platform-specific identifiers for this artifact", + "properties": { + "apple_podcast_id": { + "type": "string", + "description": "Apple Podcasts ID" + }, + "spotify_show_id": { + "type": "string", + "description": "Spotify show ID" + }, + "podcast_guid": { + "type": "string", + "description": "Podcast GUID (from RSS feed)" + }, + "youtube_video_id": { + "type": "string", + "description": "YouTube video ID" + }, + "rss_url": { + "type": "string", + "format": "uri", + "description": "RSS feed URL" + } + }, + "additionalProperties": true + } + }, + "required": ["property_id", "artifact_id", "assets"], + "additionalProperties": true, + "$defs": { + "asset_access": { + "type": "object", + "description": "Authentication for accessing secured asset URLs", + "oneOf": [ + { + "type": "object", + "description": "Bearer token authentication", + "properties": { + "method": { "type": "string", "const": "bearer_token" }, + "token": { + "type": "string", + "description": "OAuth2 bearer token for Authorization header" + } + }, + "required": ["method", "token"] + }, + { + "type": "object", + "description": "Service account authentication (GCP, AWS)", + "properties": { + "method": { "type": "string", "const": "service_account" }, + "provider": { + "type": "string", + "enum": ["gcp", "aws"], + "description": "Cloud provider" + }, + "credentials": { + "type": "object", + "description": "Service account credentials", + "additionalProperties": true + } + }, + "required": ["method", "provider"] + }, + { + "type": "object", + "description": "Pre-signed URL (credentials embedded in URL)", + "properties": { + "method": { "type": "string", "const": "signed_url" } + }, + "required": ["method"] + } + ] + } + } +} diff --git a/static/schemas/source/content-standards/calibrate-content-request.json b/static/schemas/source/content-standards/calibrate-content-request.json new file mode 100644 index 0000000000..b259e500b1 --- /dev/null +++ b/static/schemas/source/content-standards/calibrate-content-request.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/calibrate-content-request.json", + "title": "Calibrate Content Request", + "description": "Request parameters for evaluating content during calibration. Multi-turn dialogue is handled at the protocol layer via contextId.", + "type": "object", + "properties": { + "standards_id": { + "type": "string", + "description": "Standards configuration to calibrate against" + }, + "artifact": { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Artifact to evaluate" + } + }, + "required": ["standards_id", "artifact"] +} diff --git a/static/schemas/source/content-standards/calibrate-content-response.json b/static/schemas/source/content-standards/calibrate-content-response.json new file mode 100644 index 0000000000..d6e24a814b --- /dev/null +++ b/static/schemas/source/content-standards/calibrate-content-response.json @@ -0,0 +1,73 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/calibrate-content-response.json", + "title": "Calibrate Content Response", + "description": "Response payload with verdict and detailed explanations for collaborative calibration", + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Success response with detailed calibration feedback", + "properties": { + "verdict": { + "type": "string", + "enum": ["pass", "fail"], + "description": "Overall pass/fail verdict for the content evaluation" + }, + "confidence": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Model confidence in the verdict (0-1)" + }, + "explanation": { + "type": "string", + "description": "Detailed natural language explanation of the decision" + }, + "features": { + "type": "array", + "description": "Per-feature breakdown with explanations", + "items": { + "type": "object", + "properties": { + "feature_id": { + "type": "string", + "description": "Which feature was evaluated (e.g., brand_safety, brand_suitability, competitor_adjacency)" + }, + "status": { + "type": "string", + "enum": ["passed", "failed", "warning", "unevaluated"], + "description": "Evaluation status for this feature" + }, + "explanation": { + "type": "string", + "description": "Human-readable explanation of why this feature passed or failed" + } + }, + "required": ["feature_id", "status"] + } + }, + "errors": { + "not": {}, + "description": "Field must not be present in success response" + } + }, + "required": ["verdict"] + }, + { + "type": "object", + "description": "Error response", + "properties": { + "errors": { + "type": "array", + "items": { "$ref": "/schemas/core/error.json" } + }, + "verdict": { + "not": {}, + "description": "Field must not be present in error response" + } + }, + "required": ["errors"] + } + ] +} diff --git a/static/schemas/source/content-standards/content-standards.json b/static/schemas/source/content-standards/content-standards.json new file mode 100644 index 0000000000..26abbbfef1 --- /dev/null +++ b/static/schemas/source/content-standards/content-standards.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/content-standards.json", + "title": "Content Standards", + "description": "A content standards configuration defining brand safety and suitability policies. Standards are scoped by brand, geography, and channel. Multiple standards can be active simultaneously for different scopes.", + "type": "object", + "properties": { + "standards_id": { + "type": "string", + "description": "Unique identifier for this standards configuration" + }, + "name": { + "type": "string", + "description": "Human-readable name for this standards configuration" + }, + "countries_all": { + "type": "array", + "items": { "type": "string" }, + "description": "ISO 3166-1 alpha-2 country codes. Standards apply in ALL listed countries (AND logic)." + }, + "channels_any": { + "type": "array", + "items": { "$ref": "/schemas/enums/channels.json" }, + "description": "Advertising channels. Standards apply to ANY of the listed channels (OR logic)." + }, + "languages_any": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "BCP 47 language tags (e.g., 'en', 'de', 'fr'). Standards apply to content in ANY of these languages (OR logic). Content in unlisted languages is not covered by these standards." + }, + "policy": { + "type": "string", + "description": "Natural language policy describing acceptable and unacceptable content contexts. Used by LLMs and human reviewers to make judgments." + }, + "calibration_exemplars": { + "type": "object", + "description": "Training/test set to calibrate policy interpretation. Provides concrete examples of pass/fail decisions.", + "properties": { + "pass": { + "type": "array", + "items": { "$ref": "/schemas/content-standards/artifact.json" }, + "description": "Artifacts that pass the content standards" + }, + "fail": { + "type": "array", + "items": { "$ref": "/schemas/content-standards/artifact.json" }, + "description": "Artifacts that fail the content standards" + } + } + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["standards_id"] +} diff --git a/static/schemas/source/content-standards/create-content-standards-request.json b/static/schemas/source/content-standards/create-content-standards-request.json new file mode 100644 index 0000000000..bbd8c6f84f --- /dev/null +++ b/static/schemas/source/content-standards/create-content-standards-request.json @@ -0,0 +1,120 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/create-content-standards-request.json", + "title": "Create Content Standards Request", + "description": "Request parameters for creating a new content standards configuration", + "type": "object", + "properties": { + "scope": { + "type": "object", + "description": "Where this standards configuration applies", + "properties": { + "countries_all": { + "type": "array", + "items": { "type": "string" }, + "description": "ISO 3166-1 alpha-2 country codes. Standards apply in ALL listed countries (AND logic)." + }, + "channels_any": { + "type": "array", + "items": { "$ref": "/schemas/enums/channels.json" }, + "description": "Advertising channels. Standards apply to ANY of the listed channels (OR logic)." + }, + "languages_any": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "BCP 47 language tags (e.g., 'en', 'de', 'fr'). Standards apply to content in ANY of these languages (OR logic). Content in unlisted languages is not covered by these standards." + }, + "description": { + "type": "string", + "description": "Human-readable description of this scope" + } + }, + "required": ["languages_any"] + }, + "policy": { + "type": "string", + "description": "Natural language policy describing acceptable and unacceptable content contexts. Used by LLMs and human reviewers to make judgments." + }, + "calibration_exemplars": { + "type": "object", + "description": "Training/test set to calibrate policy interpretation. Use URL references for pages to be fetched and analyzed, or full artifacts for pre-extracted content.", + "properties": { + "pass": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "description": "URL reference - specific page to fetch and evaluate", + "properties": { + "type": { + "type": "string", + "const": "url", + "description": "Indicates this is a URL reference" + }, + "value": { + "type": "string", + "format": "uri", + "description": "Full URL to a specific page (e.g., 'https://espn.com/nba/story/_/id/12345/lakers-win')" + }, + "language": { + "type": "string", + "description": "BCP 47 language tag for content at this URL" + } + }, + "required": ["type", "value"] + }, + { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Full artifact with pre-extracted content (text, images, video, audio)" + } + ] + }, + "description": "Content that passes the standards" + }, + "fail": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "description": "URL reference - specific page to fetch and evaluate", + "properties": { + "type": { + "type": "string", + "const": "url", + "description": "Indicates this is a URL reference" + }, + "value": { + "type": "string", + "format": "uri", + "description": "Full URL to a specific page (e.g., 'https://news.example.com/controversial-article')" + }, + "language": { + "type": "string", + "description": "BCP 47 language tag for content at this URL" + } + }, + "required": ["type", "value"] + }, + { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Full artifact with pre-extracted content (text, images, video, audio)" + } + ] + }, + "description": "Content that fails the standards" + } + } + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["scope", "policy"], + "additionalProperties": true +} diff --git a/static/schemas/source/content-standards/create-content-standards-response.json b/static/schemas/source/content-standards/create-content-standards-response.json new file mode 100644 index 0000000000..80bdc5cdb5 --- /dev/null +++ b/static/schemas/source/content-standards/create-content-standards-response.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/create-content-standards-response.json", + "title": "Create Content Standards Response", + "description": "Response payload for creating a content standards configuration", + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Success response - returns the created standards identifier", + "properties": { + "standards_id": { + "type": "string", + "description": "Unique identifier for the created standards configuration" + }, + "errors": { + "not": {}, + "description": "Field must not be present in success response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["standards_id"] + }, + { + "type": "object", + "description": "Error response", + "properties": { + "errors": { + "type": "array", + "items": { "$ref": "/schemas/core/error.json" } + }, + "conflicting_standards_id": { + "type": "string", + "description": "If the error is a scope conflict, the ID of the existing standards that conflict" + }, + "standards_id": { + "not": {}, + "description": "Field must not be present in error response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["errors"] + } + ] +} diff --git a/static/schemas/source/content-standards/get-content-standards-request.json b/static/schemas/source/content-standards/get-content-standards-request.json new file mode 100644 index 0000000000..7790d22e0f --- /dev/null +++ b/static/schemas/source/content-standards/get-content-standards-request.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/get-content-standards-request.json", + "title": "Get Content Standards Request", + "description": "Request parameters for retrieving content safety policies", + "type": "object", + "properties": { + "standards_id": { + "type": "string", + "description": "Identifier for the standards configuration to retrieve" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["standards_id"] +} diff --git a/static/schemas/source/content-standards/get-content-standards-response.json b/static/schemas/source/content-standards/get-content-standards-response.json new file mode 100644 index 0000000000..cea2869f08 --- /dev/null +++ b/static/schemas/source/content-standards/get-content-standards-response.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/get-content-standards-response.json", + "title": "Get Content Standards Response", + "description": "Response payload with content safety policies", + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Success response - returns the content standards configuration", + "allOf": [ + { "$ref": "/schemas/content-standards/content-standards.json" } + ], + "properties": { + "errors": { + "not": {}, + "description": "Field must not be present in success response" + }, + "context": { + "$ref": "/schemas/core/context.json" + } + } + }, + { + "type": "object", + "description": "Error response", + "properties": { + "errors": { + "type": "array", + "items": { "$ref": "/schemas/core/error.json" } + }, + "standards_id": { + "not": {}, + "description": "Field must not be present in error response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["errors"] + } + ] +} diff --git a/static/schemas/source/content-standards/get-media-buy-artifacts-request.json b/static/schemas/source/content-standards/get-media-buy-artifacts-request.json new file mode 100644 index 0000000000..90a33cbb77 --- /dev/null +++ b/static/schemas/source/content-standards/get-media-buy-artifacts-request.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/get-media-buy-artifacts-request.json", + "title": "Get Media Buy Artifacts Request", + "description": "Request parameters for retrieving content artifacts from a media buy for validation", + "type": "object", + "properties": { + "media_buy_id": { + "type": "string", + "description": "Media buy to get artifacts from" + }, + "package_ids": { + "type": "array", + "items": { "type": "string" }, + "description": "Filter to specific packages within the media buy" + }, + "sampling": { + "type": "object", + "description": "Sampling parameters. Defaults to the sampling rate agreed in the media buy.", + "properties": { + "rate": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Sampling rate (0-1). 1.0 = all deliveries, 0.25 = 25% sample." + }, + "method": { + "type": "string", + "enum": ["random", "stratified", "recent", "failures_only"], + "description": "How to select the sample" + } + } + }, + "time_range": { + "type": "object", + "description": "Filter to specific time period", + "properties": { + "start": { + "type": "string", + "format": "date-time", + "description": "Start of time range (inclusive)" + }, + "end": { + "type": "string", + "format": "date-time", + "description": "End of time range (exclusive)" + } + } + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 10000, + "default": 1000, + "description": "Maximum artifacts to return per request" + }, + "cursor": { + "type": "string", + "description": "Pagination cursor for fetching subsequent pages" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["media_buy_id"] +} diff --git a/static/schemas/source/content-standards/get-media-buy-artifacts-response.json b/static/schemas/source/content-standards/get-media-buy-artifacts-response.json new file mode 100644 index 0000000000..fc1035b41b --- /dev/null +++ b/static/schemas/source/content-standards/get-media-buy-artifacts-response.json @@ -0,0 +1,142 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/get-media-buy-artifacts-response.json", + "title": "Get Media Buy Artifacts Response", + "description": "Response containing content artifacts from a media buy for validation", + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Success response with artifacts", + "properties": { + "media_buy_id": { + "type": "string", + "description": "Media buy these artifacts belong to" + }, + "artifacts": { + "type": "array", + "description": "Delivery records with full artifact content", + "items": { + "type": "object", + "properties": { + "record_id": { + "type": "string", + "description": "Unique identifier for this delivery record" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When the delivery occurred" + }, + "package_id": { + "type": "string", + "description": "Which package this delivery belongs to" + }, + "artifact": { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Full artifact with content assets" + }, + "country": { + "type": "string", + "description": "ISO 3166-1 alpha-2 country code where delivery occurred" + }, + "channel": { + "type": "string", + "description": "Channel type (e.g., display, video, audio, social)" + }, + "brand_context": { + "type": "object", + "description": "Brand information for policy evaluation. Schema TBD - placeholder for brand identifiers.", + "properties": { + "brand_id": { + "type": "string", + "description": "Brand identifier" + }, + "sku_id": { + "type": "string", + "description": "Product/SKU identifier if applicable" + } + } + }, + "local_verdict": { + "type": "string", + "enum": ["pass", "fail", "unevaluated"], + "description": "Seller's local model verdict for this artifact" + } + }, + "required": ["record_id", "artifact"] + } + }, + "sampling_info": { + "type": "object", + "description": "Information about how the sample was generated", + "properties": { + "total_deliveries": { + "type": "integer", + "description": "Total deliveries in the time range" + }, + "sampled_count": { + "type": "integer", + "description": "Number of artifacts in this response" + }, + "effective_rate": { + "type": "number", + "description": "Actual sampling rate achieved" + }, + "method": { + "type": "string", + "enum": ["random", "stratified", "recent", "failures_only"], + "description": "Sampling method used" + } + } + }, + "pagination": { + "type": "object", + "description": "Pagination information for large result sets", + "properties": { + "cursor": { + "type": "string", + "description": "Cursor for fetching the next page" + }, + "has_more": { + "type": "boolean", + "description": "Whether more results are available" + } + } + }, + "errors": { + "not": {}, + "description": "Field must not be present in success response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["media_buy_id", "artifacts"] + }, + { + "type": "object", + "description": "Error response", + "properties": { + "errors": { + "type": "array", + "items": { "$ref": "/schemas/core/error.json" } + }, + "media_buy_id": { + "not": {}, + "description": "Field must not be present in error response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["errors"] + } + ] +} diff --git a/static/schemas/source/content-standards/list-content-standards-request.json b/static/schemas/source/content-standards/list-content-standards-request.json new file mode 100644 index 0000000000..fc5f246cac --- /dev/null +++ b/static/schemas/source/content-standards/list-content-standards-request.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/list-content-standards-request.json", + "title": "List Content Standards Request", + "description": "Request parameters for listing content standards configurations", + "type": "object", + "properties": { + "channels": { + "type": "array", + "items": { "$ref": "/schemas/enums/channels.json" }, + "description": "Filter by channel" + }, + "languages": { + "type": "array", + "items": { "type": "string" }, + "description": "Filter by BCP 47 language tags" + }, + "countries": { + "type": "array", + "items": { "type": "string" }, + "description": "Filter by ISO 3166-1 alpha-2 country codes" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "additionalProperties": true +} diff --git a/static/schemas/source/content-standards/list-content-standards-response.json b/static/schemas/source/content-standards/list-content-standards-response.json new file mode 100644 index 0000000000..b436bc3f43 --- /dev/null +++ b/static/schemas/source/content-standards/list-content-standards-response.json @@ -0,0 +1,52 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/list-content-standards-response.json", + "title": "List Content Standards Response", + "description": "Response payload with list of content standards configurations", + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Success response - returns array of content standards", + "properties": { + "standards": { + "type": "array", + "items": { "$ref": "/schemas/content-standards/content-standards.json" }, + "description": "Array of content standards configurations matching the filter criteria" + }, + "errors": { + "not": {}, + "description": "Field must not be present in success response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["standards"] + }, + { + "type": "object", + "description": "Error response", + "properties": { + "errors": { + "type": "array", + "items": { "$ref": "/schemas/core/error.json" } + }, + "standards": { + "not": {}, + "description": "Field must not be present in error response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["errors"] + } + ] +} diff --git a/static/schemas/source/content-standards/update-content-standards-request.json b/static/schemas/source/content-standards/update-content-standards-request.json new file mode 100644 index 0000000000..9f0e73d27e --- /dev/null +++ b/static/schemas/source/content-standards/update-content-standards-request.json @@ -0,0 +1,123 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/update-content-standards-request.json", + "title": "Update Content Standards Request", + "description": "Request parameters for updating an existing content standards configuration. Creates a new version.", + "type": "object", + "properties": { + "standards_id": { + "type": "string", + "description": "ID of the standards configuration to update" + }, + "scope": { + "type": "object", + "description": "Updated scope for where this standards configuration applies", + "properties": { + "countries_all": { + "type": "array", + "items": { "type": "string" }, + "description": "ISO 3166-1 alpha-2 country codes. Standards apply in ALL listed countries (AND logic)." + }, + "channels_any": { + "type": "array", + "items": { "$ref": "/schemas/enums/channels.json" }, + "description": "Advertising channels. Standards apply to ANY of the listed channels (OR logic)." + }, + "languages_any": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "description": "BCP 47 language tags (e.g., 'en', 'de', 'fr'). Standards apply to content in ANY of these languages (OR logic). Content in unlisted languages is not covered by these standards." + }, + "description": { + "type": "string", + "description": "Human-readable description of this scope" + } + } + }, + "policy": { + "type": "string", + "description": "Updated natural language policy describing acceptable and unacceptable content contexts." + }, + "calibration_exemplars": { + "type": "object", + "description": "Updated training/test set to calibrate policy interpretation. Use URL references for pages to be fetched and analyzed, or full artifacts for pre-extracted content.", + "properties": { + "pass": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "description": "URL reference - specific page to fetch and evaluate", + "properties": { + "type": { + "type": "string", + "const": "url", + "description": "Indicates this is a URL reference" + }, + "value": { + "type": "string", + "format": "uri", + "description": "Full URL to a specific page (e.g., 'https://espn.com/nba/story/_/id/12345/lakers-win')" + }, + "language": { + "type": "string", + "description": "BCP 47 language tag for content at this URL" + } + }, + "required": ["type", "value"] + }, + { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Full artifact with pre-extracted content (text, images, video, audio)" + } + ] + }, + "description": "Content that passes the standards" + }, + "fail": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "description": "URL reference - specific page to fetch and evaluate", + "properties": { + "type": { + "type": "string", + "const": "url", + "description": "Indicates this is a URL reference" + }, + "value": { + "type": "string", + "format": "uri", + "description": "Full URL to a specific page (e.g., 'https://news.example.com/controversial-article')" + }, + "language": { + "type": "string", + "description": "BCP 47 language tag for content at this URL" + } + }, + "required": ["type", "value"] + }, + { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Full artifact with pre-extracted content (text, images, video, audio)" + } + ] + }, + "description": "Content that fails the standards" + } + } + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["standards_id"], + "additionalProperties": true +} diff --git a/static/schemas/source/content-standards/update-content-standards-response.json b/static/schemas/source/content-standards/update-content-standards-response.json new file mode 100644 index 0000000000..26d9d5229a --- /dev/null +++ b/static/schemas/source/content-standards/update-content-standards-response.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/update-content-standards-response.json", + "title": "Update Content Standards Response", + "description": "Response from updating a content standards configuration", + "type": "object", + "properties": { + "standards_id": { + "type": "string", + "description": "ID of the updated standards configuration" + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "Error code (e.g., STANDARDS_NOT_FOUND, SCOPE_CONFLICT)" + }, + "message": { + "type": "string", + "description": "Human-readable error message" + } + }, + "required": ["code", "message"] + }, + "description": "Errors that occurred during the update" + }, + "conflicting_standards_id": { + "type": "string", + "description": "If scope change conflicts with another configuration, the ID of the conflicting standards" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "additionalProperties": true +} diff --git a/static/schemas/source/content-standards/validate-content-delivery-request.json b/static/schemas/source/content-standards/validate-content-delivery-request.json new file mode 100644 index 0000000000..4e35137b03 --- /dev/null +++ b/static/schemas/source/content-standards/validate-content-delivery-request.json @@ -0,0 +1,80 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/validate-content-delivery-request.json", + "title": "Validate Content Delivery Request", + "description": "Request parameters for batch validating delivery records against content safety policies", + "type": "object", + "properties": { + "standards_id": { + "type": "string", + "description": "Standards configuration to validate against" + }, + "records": { + "type": "array", + "description": "Delivery records to validate (max 10,000)", + "maxItems": 10000, + "items": { + "type": "object", + "properties": { + "record_id": { + "type": "string", + "description": "Unique identifier for this delivery record" + }, + "media_buy_id": { + "type": "string", + "description": "Media buy this record belongs to (when batching across multiple buys)" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When the delivery occurred" + }, + "artifact": { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Artifact where ad was delivered" + }, + "country": { + "type": "string", + "description": "ISO 3166-1 alpha-2 country code where delivery occurred" + }, + "channel": { + "type": "string", + "description": "Channel type (e.g., display, video, audio, social)" + }, + "brand_context": { + "type": "object", + "description": "Brand information for policy evaluation. Schema TBD - placeholder for brand identifiers.", + "properties": { + "brand_id": { + "type": "string", + "description": "Brand identifier" + }, + "sku_id": { + "type": "string", + "description": "Product/SKU identifier if applicable" + } + } + } + }, + "required": ["record_id", "artifact"] + } + }, + "feature_ids": { + "type": "array", + "items": { "type": "string" }, + "description": "Specific features to evaluate (defaults to all)" + }, + "include_passed": { + "type": "boolean", + "default": true, + "description": "Include passed records in results" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["standards_id", "records"] +} diff --git a/static/schemas/source/content-standards/validate-content-delivery-response.json b/static/schemas/source/content-standards/validate-content-delivery-response.json new file mode 100644 index 0000000000..1d1feeb1cd --- /dev/null +++ b/static/schemas/source/content-standards/validate-content-delivery-response.json @@ -0,0 +1,97 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/content-standards/validate-content-delivery-response.json", + "title": "Validate Content Delivery Response", + "description": "Response payload with per-record verdicts and optional feature breakdown", + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Success response", + "properties": { + "summary": { + "type": "object", + "description": "Summary counts across all records", + "properties": { + "total_records": { "type": "integer" }, + "passed_records": { "type": "integer" }, + "failed_records": { "type": "integer" } + }, + "required": ["total_records", "passed_records", "failed_records"] + }, + "results": { + "type": "array", + "description": "Per-record evaluation results", + "items": { + "type": "object", + "properties": { + "record_id": { + "type": "string", + "description": "Which delivery record was evaluated" + }, + "verdict": { + "type": "string", + "enum": ["pass", "fail"], + "description": "Overall pass/fail verdict for this record" + }, + "features": { + "type": "array", + "description": "Optional feature-level breakdown", + "items": { + "type": "object", + "properties": { + "feature_id": { "type": "string" }, + "status": { + "type": "string", + "enum": ["passed", "failed", "warning", "unevaluated"] + }, + "value": {}, + "message": { "type": "string" }, + "rule_id": { + "type": "string", + "description": "Which rule triggered this result (e.g., GARM category, Scope3 standard)" + } + }, + "required": ["feature_id", "status"] + } + } + }, + "required": ["record_id", "verdict"] + } + }, + "errors": { + "not": {}, + "description": "Field must not be present in success response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["summary", "results"] + }, + { + "type": "object", + "description": "Error response", + "properties": { + "errors": { + "type": "array", + "items": { "$ref": "/schemas/core/error.json" } + }, + "summary": { + "not": {}, + "description": "Field must not be present in error response" + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["errors"] + } + ] +} diff --git a/static/schemas/source/core/brand-manifest.json b/static/schemas/source/core/brand-manifest.json index a1f062442c..a77b795f5b 100644 --- a/static/schemas/source/core/brand-manifest.json +++ b/static/schemas/source/core/brand-manifest.json @@ -10,6 +10,11 @@ "format": "uri", "description": "Primary brand URL for context and asset discovery. Creative agents can infer brand information from this URL." }, + "privacy_policy_url": { + "type": "string", + "format": "uri", + "description": "URL to the brand's privacy policy. Used for consumer consent flows when personal data may be shared with the advertiser. AI platforms can use this to present explicit privacy choices to users before data handoff." + }, "name": { "type": "string", "description": "Brand or business name" @@ -189,10 +194,11 @@ "enum": [ "google_merchant_center", "facebook_catalog", + "openai_product_feed", "custom" ], "default": "google_merchant_center", - "description": "Format of the product feed" + "description": "Format of the product feed. Use 'openai_product_feed' for feeds conforming to the OpenAI Commerce Product Feed specification." }, "categories": { "type": "array", @@ -215,6 +221,35 @@ "weekly" ], "description": "How frequently the product catalog is updated" + }, + "agentic_checkout": { + "type": "object", + "description": "Agentic checkout endpoint configuration. Enables AI agents to complete purchases on behalf of users through a structured checkout API.", + "properties": { + "endpoint": { + "type": "string", + "format": "uri", + "description": "Base URL for checkout session API (e.g., https://merchant.com/api/checkout_sessions)" + }, + "spec": { + "type": "string", + "enum": [ + "openai_agentic_checkout_v1" + ], + "description": "Checkout API specification implemented by the endpoint" + }, + "supported_payment_providers": { + "type": "array", + "description": "Payment providers supported by this checkout endpoint", + "items": { + "type": "string" + } + } + }, + "required": [ + "endpoint", + "spec" + ] } }, "required": [ @@ -318,6 +353,7 @@ "description": "Full brand manifest with all fields", "data": { "url": "https://acmecorp.com", + "privacy_policy_url": "https://acmecorp.com/privacy", "name": "ACME Corporation", "logos": [ { @@ -404,6 +440,27 @@ "industry": "technology", "target_audience": "business decision-makers aged 35-55" } + }, + { + "description": "E-commerce brand with OpenAI product feed and agentic checkout", + "data": { + "url": "https://shopexample.com", + "privacy_policy_url": "https://shopexample.com/privacy", + "name": "Shop Example", + "product_catalog": { + "feed_url": "https://shopexample.com/products.jsonl.gz", + "feed_format": "openai_product_feed", + "update_frequency": "daily", + "agentic_checkout": { + "endpoint": "https://api.shopexample.com/checkout_sessions", + "spec": "openai_agentic_checkout_v1", + "supported_payment_providers": [ + "stripe", + "adyen" + ] + } + } + } } ] } diff --git a/static/schemas/source/core/creative-asset.json b/static/schemas/source/core/creative-asset.json index c60f53a123..fbd302f1cb 100644 --- a/static/schemas/source/core/creative-asset.json +++ b/static/schemas/source/core/creative-asset.json @@ -96,9 +96,9 @@ "type": "string" } }, - "approved": { - "type": "boolean", - "description": "For generative creatives: set to true to approve and finalize, false to request regeneration with updated assets/message. Omit for non-generative creatives." + "status": { + "$ref": "/schemas/enums/creative-status.json", + "description": "For generative creatives: set to 'approved' to finalize, 'rejected' to request regeneration with updated assets/message. Omit for non-generative creatives (system will set based on processing state)." }, "weight": { "type": "number", diff --git a/static/schemas/source/core/creative-filters.json b/static/schemas/source/core/creative-filters.json index ce9cee5a74..3c5177a859 100644 --- a/static/schemas/source/core/creative-filters.json +++ b/static/schemas/source/core/creative-filters.json @@ -2,27 +2,19 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/core/creative-filters.json", "title": "Creative Filters", - "description": "Filter criteria for querying creative assets from the centralized library", + "description": "Filter criteria for querying creative assets from the centralized library. By default, archived creatives are excluded from results. To include archived creatives, explicitly filter by status='archived' or include 'archived' in the statuses array.", "type": "object", "properties": { - "format": { - "type": "string", - "description": "Filter by creative format type (e.g., video, audio, display)" - }, "formats": { "type": "array", - "description": "Filter by multiple creative format types", + "description": "Filter by creative format types (e.g., video, audio, display)", "items": { "type": "string" } }, - "status": { - "$ref": "/schemas/enums/creative-status.json", - "description": "Filter by creative approval status" - }, "statuses": { "type": "array", - "description": "Filter by multiple creative statuses", + "description": "Filter by creative approval statuses", "items": { "$ref": "/schemas/enums/creative-status.json" } @@ -73,10 +65,6 @@ "format": "date-time", "description": "Filter creatives last updated before this date (ISO 8601)" }, - "assigned_to_package": { - "type": "string", - "description": "Filter creatives assigned to this specific package" - }, "assigned_to_packages": { "type": "array", "description": "Filter creatives assigned to any of these packages", diff --git a/static/schemas/source/core/format.json b/static/schemas/source/core/format.json index b2bdb56a35..d6b27e708f 100644 --- a/static/schemas/source/core/format.json +++ b/static/schemas/source/core/format.json @@ -146,7 +146,8 @@ }, "assets_required": { "type": "array", - "description": "Array of required assets or asset groups for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Can contain individual assets or repeatable asset sequences (e.g., carousel products, slideshow frames).", + "deprecated": true, + "description": "DEPRECATED: Use 'assets' instead. Array of required assets or asset groups for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Can contain individual assets or repeatable asset sequences (e.g., carousel products, slideshow frames). This field is maintained for backward compatibility; new implementations should use 'assets' with the 'required' boolean on each asset.", "items": { "oneOf": [ { @@ -255,6 +256,109 @@ ] } }, + "assets": { + "type": "array", + "description": "Array of all assets supported for this format. Each asset is identified by its asset_id, which must be used as the key in creative manifests. Use the 'required' boolean on each asset to indicate whether it's mandatory. This field replaces the deprecated 'assets_required' and enables full asset discovery for buyers and AI agents.", + "items": { + "oneOf": [ + { + "description": "Individual asset specification", + "type": "object", + "properties": { + "item_type": { + "type": "string", + "const": "individual", + "description": "Discriminator indicating this is an individual asset" + }, + "asset_id": { + "type": "string", + "description": "Unique identifier for this asset. Creative manifests MUST use this exact value as the key in the assets object." + }, + "asset_type": { + "$ref": "/schemas/enums/asset-content-type.json", + "description": "Type of asset" + }, + "asset_role": { + "type": "string", + "description": "Optional descriptive label for this asset's purpose (e.g., 'hero_image', 'logo', 'third_party_tracking'). Not used for referencing assets in manifestsβ€”use asset_id instead. This field is for human-readable documentation and UI display only." + }, + "required": { + "type": "boolean", + "description": "Whether this asset is required (true) or optional (false). Required assets must be provided for a valid creative. Optional assets enhance the creative but are not mandatory." + }, + "requirements": { + "type": "object", + "description": "Technical requirements for this asset (dimensions, file size, duration, etc.). For template formats, use parameters_from_format_id: true to indicate asset parameters must match the format_id parameters (width/height/unit and/or duration_ms).", + "additionalProperties": true + } + }, + "required": ["item_type", "asset_id", "asset_type", "required"] + }, + { + "description": "Repeatable asset group (for carousels, slideshows, playlists, etc.)", + "type": "object", + "properties": { + "item_type": { + "type": "string", + "const": "repeatable_group", + "description": "Discriminator indicating this is a repeatable asset group" + }, + "asset_group_id": { + "type": "string", + "description": "Identifier for this asset group (e.g., 'product', 'slide', 'card')" + }, + "required": { + "type": "boolean", + "description": "Whether this asset group is required. If true, at least min_count repetitions must be provided." + }, + "min_count": { + "type": "integer", + "description": "Minimum number of repetitions required (if group is required) or allowed (if optional)", + "minimum": 0 + }, + "max_count": { + "type": "integer", + "description": "Maximum number of repetitions allowed", + "minimum": 1 + }, + "assets": { + "type": "array", + "description": "Assets within each repetition of this group", + "items": { + "type": "object", + "properties": { + "asset_id": { + "type": "string", + "description": "Identifier for this asset within the group" + }, + "asset_type": { + "$ref": "/schemas/enums/asset-content-type.json", + "description": "Type of asset" + }, + "asset_role": { + "type": "string", + "description": "Optional descriptive label for this asset's purpose. Not used for referencing assets in manifestsβ€”use asset_id instead. This field is for human-readable documentation and UI display only." + }, + "required": { + "type": "boolean", + "description": "Whether this asset is required within each repetition of the group", + "default": false + }, + "requirements": { + "type": "object", + "description": "Technical requirements for this asset. For template formats, use parameters_from_format_id: true to indicate asset parameters must match the format_id parameters (width/height/unit and/or duration_ms).", + "additionalProperties": true + } + }, + "required": ["asset_id", "asset_type", "required"] + } + } + }, + "required": ["item_type", "asset_group_id", "required", "min_count", "max_count", "assets"] + } + ] + } + }, "delivery": { "type": "object", "description": "Delivery method specifications (e.g., hosted, VAST, third-party tags)", diff --git a/static/schemas/source/core/identifier.json b/static/schemas/source/core/identifier.json new file mode 100644 index 0000000000..f7e68aabe2 --- /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/product-filters.json b/static/schemas/source/core/product-filters.json index 492ffcee08..20aa1daa6f 100644 --- a/static/schemas/source/core/product-filters.json +++ b/static/schemas/source/core/product-filters.json @@ -84,18 +84,79 @@ }, "countries": { "type": "array", - "description": "Filter by target countries using ISO 3166-1 alpha-2 country codes (e.g., ['US', 'CA', 'GB'])", + "description": "Filter by country coverage using ISO 3166-1 alpha-2 codes (e.g., ['US', 'CA', 'GB']). Works for all inventory types.", "items": { "type": "string", "pattern": "^[A-Z]{2}$" } }, + "regions": { + "type": "array", + "description": "Filter by region coverage using ISO 3166-2 codes (e.g., ['US-NY', 'US-CA', 'GB-SCT']). Use for locally-bound inventory (regional OOH, local TV) where products have region-specific coverage.", + "items": { + "type": "string", + "pattern": "^[A-Z]{2}-[A-Z0-9]+$" + } + }, + "metros": { + "type": "array", + "description": "Filter by metro coverage for locally-bound inventory (radio, DOOH, local TV). Use when products have DMA/metro-specific coverage. For digital inventory where products have broad coverage, use required_geo_targeting instead to filter by seller capability.", + "items": { + "type": "object", + "properties": { + "system": { + "$ref": "/schemas/enums/metro-system.json", + "description": "Metro classification system" + }, + "code": { + "type": "string", + "description": "Metro code within the system (e.g., '501' for NYC DMA)" + } + }, + "required": ["system", "code"], + "additionalProperties": false + } + }, "channels": { "type": "array", "description": "Filter by advertising channels (e.g., ['display', 'video', 'dooh'])", "items": { "$ref": "/schemas/enums/channels.json" } + }, + "required_axe_integrations": { + "type": "array", + "description": "Filter to products executable through specific agentic ad exchanges. URLs are canonical identifiers.", + "items": { + "type": "string", + "format": "uri" + } + }, + "required_features": { + "type": "array", + "description": "Filter to products from sellers supporting specific protocol features (e.g., 'content_standards', 'inline_creative_management')", + "items": { + "type": "string" + } + }, + "required_geo_targeting": { + "type": "array", + "description": "Filter to products from sellers supporting specific geo targeting capabilities. Each entry specifies a targeting level (country, region, metro, postal_area) and optionally a system for levels that have multiple classification systems.", + "items": { + "type": "object", + "properties": { + "level": { + "$ref": "/schemas/enums/geo-level.json", + "description": "Geographic targeting level (country, region, metro, postal_area)" + }, + "system": { + "type": "string", + "description": "Classification system within the level. Required for metro (e.g., 'nielsen_dma') and postal_area (e.g., 'us_zip'). Not applicable for country/region which use ISO standards." + } + }, + "required": ["level"], + "additionalProperties": false + } } }, "additionalProperties": true 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 0000000000..06e5ced9ba --- /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/core/reporting-webhook.json b/static/schemas/source/core/reporting-webhook.json new file mode 100644 index 0000000000..cb9a295533 --- /dev/null +++ b/static/schemas/source/core/reporting-webhook.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/core/reporting-webhook.json", + "title": "Reporting Webhook", + "description": "Webhook configuration for automated reporting delivery. Configures where and how campaign performance reports are sent.", + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "Webhook endpoint URL for reporting notifications" + }, + "token": { + "type": "string", + "description": "Optional client-provided token for webhook validation. Echoed back in webhook payload to validate request authenticity.", + "minLength": 16 + }, + "authentication": { + "type": "object", + "description": "Authentication configuration for webhook delivery (A2A-compatible)", + "properties": { + "schemes": { + "type": "array", + "description": "Array of authentication schemes. Supported: ['Bearer'] for simple token auth, ['HMAC-SHA256'] for signature verification (recommended for production)", + "items": { + "$ref": "/schemas/enums/auth-scheme.json" + }, + "minItems": 1, + "maxItems": 1 + }, + "credentials": { + "type": "string", + "description": "Credentials for authentication. For Bearer: token sent in Authorization header. For HMAC-SHA256: shared secret used to generate signature. Minimum 32 characters. Exchanged out-of-band during onboarding.", + "minLength": 32 + } + }, + "required": [ + "schemes", + "credentials" + ], + "additionalProperties": false + }, + "reporting_frequency": { + "type": "string", + "enum": [ + "hourly", + "daily", + "monthly" + ], + "description": "Frequency for automated reporting delivery. Must be supported by all products in the media buy." + }, + "requested_metrics": { + "type": "array", + "description": "Optional list of metrics to include in webhook notifications. If omitted, all available metrics are included. Must be subset of product's available_metrics.", + "items": { + "$ref": "/schemas/enums/available-metric.json" + }, + "uniqueItems": true + } + }, + "required": [ + "url", + "authentication", + "reporting_frequency" + ], + "additionalProperties": true +} diff --git a/static/schemas/source/core/targeting.json b/static/schemas/source/core/targeting.json index f2f6972057..a3adfc74f0 100644 --- a/static/schemas/source/core/targeting.json +++ b/static/schemas/source/core/targeting.json @@ -5,33 +5,66 @@ "description": "Optional geographic refinements for media buys. Most targeting should be expressed in the brief and handled by the publisher. These fields are primarily for geographic restrictions (RCT testing, regulatory compliance).", "type": "object", "properties": { - "geo_country_any_of": { + "geo_countries": { "type": "array", - "description": "Restrict delivery to specific countries (ISO codes). Use for regulatory compliance or RCT testing.", + "description": "Restrict delivery to specific countries. ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').", "items": { "type": "string", "pattern": "^[A-Z]{2}$" } }, - "geo_region_any_of": { + "geo_regions": { "type": "array", - "description": "Restrict delivery to specific regions/states. Use for regulatory compliance or RCT testing.", + "description": "Restrict delivery to specific regions/states. ISO 3166-2 subdivision codes (e.g., 'US-CA', 'GB-SCT').", "items": { - "type": "string" + "type": "string", + "pattern": "^[A-Z]{2}-[A-Z0-9]{1,3}$" } }, - "geo_metro_any_of": { + "geo_metros": { "type": "array", - "description": "Restrict delivery to specific metro areas (DMA codes). Use for regulatory compliance or RCT testing.", + "description": "Restrict delivery to specific metro areas. Each entry specifies the classification system and target values. Seller must declare supported systems in get_adcp_capabilities.", "items": { - "type": "string" + "type": "object", + "properties": { + "system": { + "$ref": "/schemas/enums/metro-system.json", + "description": "Metro area classification system (e.g., 'nielsen_dma', 'uk_itl2')" + }, + "values": { + "type": "array", + "description": "Metro codes within the system (e.g., ['501', '602'] for Nielsen DMAs)", + "items": { + "type": "string" + }, + "minItems": 1 + } + }, + "required": ["system", "values"], + "additionalProperties": false } }, - "geo_postal_code_any_of": { + "geo_postal_areas": { "type": "array", - "description": "Restrict delivery to specific postal/ZIP codes. Use for regulatory compliance or RCT testing.", + "description": "Restrict delivery to specific postal areas. Each entry specifies the postal system and target values. Seller must declare supported systems in get_adcp_capabilities.", "items": { - "type": "string" + "type": "object", + "properties": { + "system": { + "$ref": "/schemas/enums/postal-system.json", + "description": "Postal code system (e.g., 'us_zip', 'gb_outward'). System name encodes country and precision." + }, + "values": { + "type": "array", + "description": "Postal codes within the system (e.g., ['10001', '10002'] for us_zip)", + "items": { + "type": "string" + }, + "minItems": 1 + } + }, + "required": ["system", "values"], + "additionalProperties": false } }, "axe_include_segment": { diff --git a/static/schemas/source/enums/adcp-domain.json b/static/schemas/source/enums/adcp-domain.json index 68db230066..ee392aeff0 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/channels.json b/static/schemas/source/enums/channels.json index a6bc0b24c7..e0005063a5 100644 --- a/static/schemas/source/enums/channels.json +++ b/static/schemas/source/enums/channels.json @@ -1,18 +1,49 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/enums/channels.json", - "title": "Advertising Channels", - "description": "Standard advertising channels supported by AdCP", + "title": "Media Channel", + "description": "Standardized advertising media channels describing how buyers allocate budget. Channels are planning abstractions, not technical substrates. See the Media Channel Taxonomy specification for detailed definitions.", "type": "string", "enum": [ "display", - "video", - "audio", - "native", - "dooh", + "olv", + "social", + "search", "ctv", + "linear_tv", + "radio", + "streaming_audio", "podcast", - "retail", - "social" - ] + "dooh", + "ooh", + "print", + "cinema", + "email", + "gaming", + "retail_media", + "influencer", + "affiliate", + "product_placement" + ], + "enumDescriptions": { + "display": "Digital display advertising (banners, native, rich media) across web and app", + "olv": "Online video advertising outside CTV (pre-roll, outstream, in-app video)", + "social": "Social media platforms (Facebook, Instagram, TikTok, LinkedIn, etc.)", + "search": "Search engine advertising and search networks", + "ctv": "Connected TV and streaming on television screens", + "linear_tv": "Traditional broadcast and cable television", + "radio": "Traditional AM/FM radio broadcast", + "streaming_audio": "Digital audio streaming services (Spotify, Pandora, etc.)", + "podcast": "Podcast advertising (host-read or dynamically inserted)", + "dooh": "Digital out-of-home screens in public spaces", + "ooh": "Classic out-of-home (physical billboards, transit, etc.)", + "print": "Newspapers, magazines, and other print publications", + "cinema": "Movie theater advertising", + "email": "Email advertising and sponsored newsletter content", + "gaming": "In-game advertising across platforms", + "retail_media": "Retail media networks and commerce marketplaces (Amazon, Walmart, Instacart)", + "influencer": "Creator and influencer marketing partnerships", + "affiliate": "Affiliate networks, comparison sites, and performance-based partnerships", + "product_placement": "Product placement, branded content, and sponsorship integrations" + } } diff --git a/static/schemas/source/enums/creative-status.json b/static/schemas/source/enums/creative-status.json index 9c9cf0f903..ed0b231368 100644 --- a/static/schemas/source/enums/creative-status.json +++ b/static/schemas/source/enums/creative-status.json @@ -8,12 +8,14 @@ "processing", "approved", "rejected", - "pending_review" + "pending_review", + "archived" ], "enumDescriptions": { "processing": "Creative is being processed or transcoded", "approved": "Creative has been approved and is ready for delivery", "rejected": "Creative has been rejected due to policy or technical issues", - "pending_review": "Creative is under review" + "pending_review": "Creative is under review", + "archived": "Creative has been archived and is excluded from default queries" } } diff --git a/static/schemas/source/enums/geo-level.json b/static/schemas/source/enums/geo-level.json new file mode 100644 index 0000000000..32864a99c4 --- /dev/null +++ b/static/schemas/source/enums/geo-level.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/enums/geo-level.json", + "title": "Geographic Targeting Level", + "description": "Geographic targeting granularity levels. Some levels (metro, postal_area) require a system specification.", + "type": "string", + "enum": [ + "country", + "region", + "metro", + "postal_area" + ] +} diff --git a/static/schemas/source/enums/metro-system.json b/static/schemas/source/enums/metro-system.json new file mode 100644 index 0000000000..3494ff8442 --- /dev/null +++ b/static/schemas/source/enums/metro-system.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/enums/metro-system.json", + "title": "Metro Area System", + "description": "Metro area classification systems for geographic targeting", + "type": "string", + "enum": [ + "nielsen_dma", + "uk_itl1", + "uk_itl2", + "eurostat_nuts2", + "custom" + ] +} diff --git a/static/schemas/source/enums/postal-system.json b/static/schemas/source/enums/postal-system.json new file mode 100644 index 0000000000..ae3b08f66b --- /dev/null +++ b/static/schemas/source/enums/postal-system.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/enums/postal-system.json", + "title": "Postal Code System", + "description": "Postal code systems for geographic targeting. System names encode country and precision level.", + "type": "string", + "enum": [ + "us_zip", + "us_zip_plus_four", + "gb_outward", + "gb_full", + "ca_fsa", + "ca_full", + "de_plz", + "fr_code_postal", + "au_postcode" + ] +} diff --git a/static/schemas/source/enums/property-type.json b/static/schemas/source/enums/property-type.json index 0b0d104714..9bf4d054a2 100644 --- a/static/schemas/source/enums/property-type.json +++ b/static/schemas/source/enums/property-type.json @@ -2,15 +2,26 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/enums/property-type.json", "title": "Property Type", - "description": "Types of advertising properties that can be represented in AdCP", + "description": "Types of addressable advertising properties with verifiable ownership. Property types are a subset of media channels - they represent inventory surfaces that can be validated via adagents.json.", "type": "string", "enum": [ "website", "mobile_app", "ctv_app", + "desktop_app", "dooh", "podcast", "radio", "streaming_audio" - ] + ], + "enumDescriptions": { + "website": "Web properties accessible via browser, identified by domain", + "mobile_app": "Native mobile applications, identified by app store IDs or bundle identifiers", + "ctv_app": "Connected TV applications, identified by platform-specific store IDs", + "desktop_app": "Desktop applications (Electron, native, Chromium wrappers), identified by application bundle IDs", + "dooh": "Digital out-of-home screen networks, identified by network/venue IDs", + "podcast": "Podcast feeds and episodes, identified by RSS feed URLs or podcast IDs", + "radio": "Radio station properties, identified by call signs or station IDs", + "streaming_audio": "Digital audio streaming properties, identified by platform-specific IDs" + } } diff --git a/static/schemas/source/enums/task-type.json b/static/schemas/source/enums/task-type.json index 063291bb42..7af80e9b9d 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/extensions/extension-meta.json b/static/schemas/source/extensions/extension-meta.json new file mode 100644 index 0000000000..75e199a579 --- /dev/null +++ b/static/schemas/source/extensions/extension-meta.json @@ -0,0 +1,59 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/extensions/extension-meta.json", + "title": "AdCP Extension File Schema", + "description": "Schema that all extension files must follow. Combines metadata (valid_from, docs_url) with the actual extension data schema. Extensions are auto-discovered from /schemas/extensions/*.json and included in versioned builds based on valid_from/valid_until.", + "type": "object", + "properties": { + "$schema": { + "type": "string", + "const": "http://json-schema.org/draft-07/schema#" + }, + "$id": { + "type": "string", + "pattern": "^/schemas/extensions/[a-z][a-z0-9_]*\\.json$", + "description": "Extension ID following pattern /schemas/extensions/{namespace}.json" + }, + "title": { + "type": "string", + "description": "Human-readable title for the extension" + }, + "description": { + "type": "string", + "description": "Description of what this extension provides" + }, + "valid_from": { + "type": "string", + "pattern": "^\\d+\\.\\d+$", + "description": "Minimum AdCP version this extension is compatible with (e.g., '2.5'). Extension will be included in all versioned schema builds >= this version." + }, + "valid_until": { + "type": "string", + "pattern": "^\\d+\\.\\d+$", + "description": "Last AdCP version this extension is compatible with (e.g., '3.0'). Omit if extension is still valid for current and future versions." + }, + "docs_url": { + "type": "string", + "format": "uri", + "description": "URL to documentation for implementors of this extension" + }, + "type": { + "const": "object", + "description": "Extensions must be objects (data within ext.{namespace})" + }, + "properties": { + "type": "object", + "description": "Schema properties defining the structure of ext.{namespace} data", + "additionalProperties": true + }, + "required": { + "type": "array", + "items": { "type": "string" }, + "description": "Required properties within the extension data" + }, + "additionalProperties": { + "description": "Whether additional properties are allowed in the extension data" + } + }, + "required": ["$schema", "$id", "title", "description", "valid_from", "type", "properties"] +} diff --git a/static/schemas/source/extensions/index.json b/static/schemas/source/extensions/index.json new file mode 100644 index 0000000000..9b393e9ad2 --- /dev/null +++ b/static/schemas/source/extensions/index.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/extensions/index.json", + "title": "AdCP Extension Registry", + "description": "Auto-generated registry of formal AdCP extensions. Extensions provide typed schemas for vendor-specific or domain-specific data within the ext field. Agents declare which extensions they support in their agent card.", + "_generated": true, + "_note": "This file is auto-generated by the build process. Do not edit manually. Add extensions by creating new .json files in this directory following extension-meta.json schema.", + "extensions": {} +} diff --git a/static/schemas/source/index.json b/static/schemas/source/index.json index 694e692c34..841f6ec034 100644 --- a/static/schemas/source/index.json +++ b/static/schemas/source/index.json @@ -4,7 +4,7 @@ "title": "AdCP Schema Registry v1", "version": "1.0.0", "description": "Registry of all AdCP JSON schemas for validation and discovery", - "adcp_version": "2.5.3", + "adcp_version": "3.0.0-beta.1", "standard_formats_version": "2.0.0", "versioning": { "note": "AdCP uses path-based versioning. The schema URL path (/schemas/) indicates the version. Individual request/response schemas do NOT include adcp_version fields. Compatibility follows semantic versioning rules." @@ -31,7 +31,7 @@ ] } }, - "lastUpdated": "2025-11-22", + "lastUpdated": "2025-01-23", "baseUrl": "/schemas/v1", "schemas": { "core": { @@ -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", @@ -353,6 +361,18 @@ "creative-sort-field": { "$ref": "/schemas/enums/creative-sort-field.json", "description": "Fields available for sorting creative listings" + }, + "geo-level": { + "$ref": "/schemas/enums/geo-level.json", + "description": "Geographic targeting granularity levels (country, region, metro, postal_area)" + }, + "metro-system": { + "$ref": "/schemas/enums/metro-system.json", + "description": "Metro area classification systems for geographic targeting (nielsen_dma, uk_itl1, uk_itl2, eurostat_nuts2)" + }, + "postal-system": { + "$ref": "/schemas/enums/postal-system.json", + "description": "Postal code systems for geographic targeting. System names encode country and precision (us_zip, gb_outward, ca_fsa, etc.)" } } }, @@ -403,6 +423,10 @@ "package-request": { "$ref": "/schemas/media-buy/package-request.json", "description": "Package configuration for media buy creation - used within create_media_buy request" + }, + "package-update": { + "$ref": "/schemas/media-buy/package-update.json", + "description": "Package update configuration for update_media_buy - identifies package and specifies fields to modify" } }, "tasks": { @@ -477,6 +501,8 @@ } }, "list-authorized-properties": { + "deprecated": true, + "deprecation_note": "Replaced by get_capabilities which provides expanded capability declaration", "request": { "$ref": "/schemas/media-buy/list-authorized-properties-request.json", "description": "Request parameters for discovering all properties this agent is authorized to represent" @@ -562,6 +588,192 @@ } } }, + "governance": { + "description": "Governance protocol for property governance, brand standards, content 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" + }, + "content-standards": { + "$ref": "/schemas/content-standards/content-standards.json", + "description": "Reusable content standards configuration - defines brand safety/suitability policies with scope, policy, calibration exemplars, and lifecycle dates" + }, + "content-standards-artifact": { + "$ref": "/schemas/content-standards/artifact.json", + "description": "Content artifact for evaluation or calibration - represents content context where ad placements occur, identified by property_id + artifact_id" + }, + "artifact-webhook-payload": { + "$ref": "/schemas/content-standards/artifact-webhook-payload.json", + "description": "Webhook payload for content artifact delivery from sales agents to orchestrators" + } + }, + "tasks": { + "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" + } + }, + "list-content-standards": { + "request": { + "$ref": "/schemas/content-standards/list-content-standards-request.json", + "description": "Request parameters for listing content standards configurations" + }, + "response": { + "$ref": "/schemas/content-standards/list-content-standards-response.json", + "description": "Response payload with list of content standards configurations" + } + }, + "get-content-standards": { + "request": { + "$ref": "/schemas/content-standards/get-content-standards-request.json", + "description": "Request parameters for retrieving a specific standards configuration" + }, + "response": { + "$ref": "/schemas/content-standards/get-content-standards-response.json", + "description": "Response payload with full standards configuration including policy and calibration data" + } + }, + "create-content-standards": { + "request": { + "$ref": "/schemas/content-standards/create-content-standards-request.json", + "description": "Request parameters for creating a new content standards configuration" + }, + "response": { + "$ref": "/schemas/content-standards/create-content-standards-response.json", + "description": "Response payload with new standards_id" + } + }, + "update-content-standards": { + "request": { + "$ref": "/schemas/content-standards/update-content-standards-request.json", + "description": "Request parameters for updating an existing content standards configuration" + }, + "response": { + "$ref": "/schemas/content-standards/update-content-standards-response.json", + "description": "Response payload confirming update" + } + }, + "calibrate-content": { + "request": { + "$ref": "/schemas/content-standards/calibrate-content-request.json", + "description": "Request parameters for collaborative calibration dialogue" + }, + "response": { + "$ref": "/schemas/content-standards/calibrate-content-response.json", + "description": "Response payload with detailed explanations for policy alignment" + } + }, + "validate-content-delivery": { + "request": { + "$ref": "/schemas/content-standards/validate-content-delivery-request.json", + "description": "Request parameters for batch validating delivery records" + }, + "response": { + "$ref": "/schemas/content-standards/validate-content-delivery-response.json", + "description": "Response payload with batch validation results" + } + }, + "get-media-buy-artifacts": { + "request": { + "$ref": "/schemas/content-standards/get-media-buy-artifacts-request.json", + "description": "Request parameters for retrieving content artifacts from a media buy" + }, + "response": { + "$ref": "/schemas/content-standards/get-media-buy-artifacts-response.json", + "description": "Response payload with content artifacts for validation" + } + } + } + }, + "protocol": { + "description": "Protocol-level tasks that work across all AdCP domain protocols", + "tasks": { + "get-adcp-capabilities": { + "request": { + "$ref": "/schemas/protocol/get-adcp-capabilities-request.json", + "description": "Request parameters for cross-protocol capability discovery" + }, + "response": { + "$ref": "/schemas/protocol/get-adcp-capabilities-response.json", + "description": "Response payload for get_adcp_capabilities task - includes AdCP version, supported protocols, and protocol-specific capabilities (media_buy, signals, etc.)" + } + } + } + }, "adagents": { "description": "Authorized sales agents file format specification", "$ref": "/schemas/adagents.json", @@ -573,9 +785,23 @@ "schemas": { "adcp-extension": { "$ref": "/schemas/protocols/adcp-extension.json", - "description": "AdCP extension for agent cards (MCP and A2A). Declares AdCP version and supported protocol domains." + "deprecated": true, + "deprecation_note": "Use tool-based discovery instead. Check for 'get_adcp_capabilities' tool, then call it for full capability details.", + "description": "DEPRECATED: AdCP extension for agent cards. Use get_adcp_capabilities tool for discovery instead." } } + }, + "extensions": { + "description": "Typed extension schemas for vendor-specific or domain-specific data. Extensions define the structure of data within the ext.{namespace} field. Agents declare which extensions they support in their agent card.", + "registry": { + "$ref": "/schemas/extensions/index.json", + "description": "Auto-generated registry of all available extensions with metadata" + }, + "meta": { + "$ref": "/schemas/extensions/extension-meta.json", + "description": "Schema that all extension files must follow. Defines valid_from, valid_until, and extension data structure." + }, + "schemas": {} } }, "usage": { diff --git a/static/schemas/source/media-buy/create-media-buy-request.json b/static/schemas/source/media-buy/create-media-buy-request.json index 3e83f44baf..648a9bcb3b 100644 --- a/static/schemas/source/media-buy/create-media-buy-request.json +++ b/static/schemas/source/media-buy/create-media-buy-request.json @@ -33,14 +33,18 @@ "description": "Campaign end date/time in ISO 8601 format" }, "reporting_webhook": { - "$comment": "Fields url, token, and authentication mirror push-notification-config.json. Inlined here because allOf + additionalProperties:false doesn't work in JSON Schema (additionalProperties can't see $ref properties). See CLAUDE.md 'Avoiding allOf with additionalProperties'.", + "$ref": "/schemas/core/reporting-webhook.json", + "description": "Optional webhook configuration for automated reporting delivery" + }, + "artifact_webhook": { + "$comment": "Webhook configuration for content artifact delivery - enables governance validation. Same authentication structure as reporting_webhook.", "type": "object", - "description": "Optional webhook configuration for automated reporting delivery. Combines push_notification_config structure with reporting-specific fields.", + "description": "Optional webhook configuration for content artifact delivery. Used by governance agents to validate content adjacency. Seller pushes artifacts to this endpoint; orchestrator forwards to governance agent for validation.", "properties": { "url": { "type": "string", "format": "uri", - "description": "Webhook endpoint URL for reporting notifications" + "description": "Webhook endpoint URL for artifact delivery" }, "token": { "type": "string", @@ -72,39 +76,33 @@ ], "additionalProperties": false }, - "reporting_frequency": { + "delivery_mode": { + "type": "string", + "enum": [ + "realtime", + "batched" + ], + "description": "How artifacts are delivered. 'realtime' pushes artifacts as impressions occur. 'batched' aggregates artifacts and pushes periodically (see batch_frequency)." + }, + "batch_frequency": { "type": "string", "enum": [ "hourly", - "daily", - "monthly" + "daily" ], - "description": "Frequency for automated reporting delivery. Must be supported by all products in the media buy." + "description": "For batched delivery, how often to push artifacts. Required when delivery_mode is 'batched'." }, - "requested_metrics": { - "type": "array", - "description": "Optional list of metrics to include in webhook notifications. If omitted, all available metrics are included. Must be subset of product's available_metrics.", - "items": { - "type": "string", - "enum": [ - "impressions", - "spend", - "clicks", - "ctr", - "video_completions", - "completion_rate", - "conversions", - "viewability", - "engagement_rate" - ] - }, - "uniqueItems": true + "sampling_rate": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Fraction of impressions to include (0-1). 1.0 = all impressions, 0.1 = 10% sample. Default: 1.0" } }, "required": [ "url", "authentication", - "reporting_frequency" + "delivery_mode" ], "additionalProperties": true }, diff --git a/static/schemas/source/media-buy/get-products-request.json b/static/schemas/source/media-buy/get-products-request.json index 1c7483fd84..02817c0f38 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 3a54bd3c8e..692787b91c 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/media-buy/list-creatives-response.json b/static/schemas/source/media-buy/list-creatives-response.json index a5fde42279..1b05b2d3ff 100644 --- a/static/schemas/source/media-buy/list-creatives-response.json +++ b/static/schemas/source/media-buy/list-creatives-response.json @@ -182,29 +182,19 @@ "type": "string", "description": "Package identifier" }, - "package_name": { + "buyer_ref": { "type": "string", - "description": "Human-readable package name" + "description": "Buyer's reference identifier for this package" }, "assigned_date": { "type": "string", "format": "date-time", "description": "When this assignment was created" - }, - "status": { - "type": "string", - "enum": [ - "active", - "paused", - "ended" - ], - "description": "Status of this specific assignment" } }, "required": [ "package_id", - "assigned_date", - "status" + "assigned_date" ], "additionalProperties": true } @@ -293,6 +283,11 @@ "type": "object", "description": "Breakdown of creatives by status", "properties": { + "processing": { + "type": "integer", + "description": "Number of creatives being processed", + "minimum": 0 + }, "approved": { "type": "integer", "description": "Number of approved creatives", @@ -416,9 +411,8 @@ "assigned_packages": [ { "package_id": "pkg_ctv_001", - "package_name": "CTV Prime Time", - "assigned_date": "2024-01-16T09:00:00Z", - "status": "active" + "buyer_ref": "ctv_prime_time_q1", + "assigned_date": "2024-01-16T09:00:00Z" } ] }, diff --git a/static/schemas/source/media-buy/package-request.json b/static/schemas/source/media-buy/package-request.json index c09f6fa5f0..a1e94bcaa2 100644 --- a/static/schemas/source/media-buy/package-request.json +++ b/static/schemas/source/media-buy/package-request.json @@ -51,16 +51,16 @@ "targeting_overlay": { "$ref": "/schemas/core/targeting.json" }, - "creative_ids": { + "creative_assignments": { "type": "array", - "description": "Creative IDs to assign to this package at creation time (references existing library creatives)", + "description": "Assign existing library creatives to this package with optional weights and placement targeting", "items": { - "type": "string" + "$ref": "/schemas/core/creative-assignment.json" } }, "creatives": { "type": "array", - "description": "Full creative objects to upload and assign to this package at creation time (alternative to creative_ids - creatives will be added to library). Supports both static and generative creatives.", + "description": "Upload new creative assets and assign to this package (creatives will be added to library). Use creative_assignments instead for existing library creatives.", "items": { "$ref": "/schemas/core/creative-asset.json" }, diff --git a/static/schemas/source/media-buy/package-update.json b/static/schemas/source/media-buy/package-update.json new file mode 100644 index 0000000000..a93f87834a --- /dev/null +++ b/static/schemas/source/media-buy/package-update.json @@ -0,0 +1,73 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/media-buy/package-update.json", + "title": "Package Update", + "description": "Package update configuration for update_media_buy. Identifies package by package_id or buyer_ref and specifies fields to modify. Fields not present are left unchanged. Note: product_id, format_ids, and pricing_option_id cannot be changed after creation.", + "type": "object", + "properties": { + "package_id": { + "type": "string", + "description": "Publisher's ID of package to update" + }, + "buyer_ref": { + "type": "string", + "description": "Buyer's reference for the package to update" + }, + "budget": { + "type": "number", + "description": "Updated budget allocation for this package in the currency specified by the pricing option", + "minimum": 0 + }, + "pacing": { + "$ref": "/schemas/enums/pacing.json" + }, + "bid_price": { + "type": "number", + "description": "Updated bid price for auction-based pricing options (only applies when pricing_option is auction-based)", + "minimum": 0 + }, + "impressions": { + "type": "number", + "description": "Updated impression goal for this package", + "minimum": 0 + }, + "paused": { + "type": "boolean", + "description": "Pause/resume specific package (true = paused, false = active)" + }, + "targeting_overlay": { + "$ref": "/schemas/core/targeting.json" + }, + "creative_assignments": { + "type": "array", + "description": "Replace creative assignments for this package with optional weights and placement targeting. Uses replacement semantics - omit to leave assignments unchanged.", + "items": { + "$ref": "/schemas/core/creative-assignment.json" + } + }, + "creatives": { + "type": "array", + "description": "Upload new creative assets and assign to this package (creatives will be added to library). Use creative_assignments instead for existing library creatives.", + "items": { + "$ref": "/schemas/core/creative-asset.json" + }, + "maxItems": 100 + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "oneOf": [ + { + "required": [ + "package_id" + ] + }, + { + "required": [ + "buyer_ref" + ] + } + ], + "additionalProperties": true +} diff --git a/static/schemas/source/media-buy/update-media-buy-request.json b/static/schemas/source/media-buy/update-media-buy-request.json index e88dd17b5e..67c108a10f 100644 --- a/static/schemas/source/media-buy/update-media-buy-request.json +++ b/static/schemas/source/media-buy/update-media-buy-request.json @@ -29,82 +29,16 @@ "type": "array", "description": "Package-specific updates", "items": { - "type": "object", - "properties": { - "package_id": { - "type": "string", - "description": "Publisher's ID of package to update" - }, - "buyer_ref": { - "type": "string", - "description": "Buyer's reference for the package to update" - }, - "budget": { - "type": "number", - "description": "Updated budget allocation for this package in the currency specified by the pricing option", - "minimum": 0 - }, - "pacing": { - "$ref": "/schemas/enums/pacing.json" - }, - "bid_price": { - "type": "number", - "description": "Updated bid price for auction-based pricing options (only applies when pricing_option is auction-based)", - "minimum": 0 - }, - "impressions": { - "type": "number", - "description": "Updated impression goal for this package", - "minimum": 0 - }, - "paused": { - "type": "boolean", - "description": "Pause/resume specific package (true = paused, false = active)" - }, - "targeting_overlay": { - "$ref": "/schemas/core/targeting.json" - }, - "creative_ids": { - "type": "array", - "description": "Update creative assignments (references existing library creatives)", - "items": { - "type": "string" - } - }, - "creatives": { - "type": "array", - "description": "Full creative objects to upload and assign to this package (alternative to creative_ids - creatives will be added to library). Supports both static and generative creatives.", - "items": { - "$ref": "/schemas/core/creative-asset.json" - }, - "maxItems": 100 - }, - "creative_assignments": { - "type": "array", - "description": "Full creative assignment objects with weights and placement targeting (alternative to creative_ids - provides granular control over weights and placement targeting). Uses replacement semantics like creative_ids.", - "items": { - "$ref": "/schemas/core/creative-assignment.json" - } - } - }, - "oneOf": [ - { - "required": [ - "package_id" - ] - }, - { - "required": [ - "buyer_ref" - ] - } - ], - "additionalProperties": true + "$ref": "/schemas/media-buy/package-update.json" } }, + "reporting_webhook": { + "$ref": "/schemas/core/reporting-webhook.json", + "description": "Optional webhook configuration for automated reporting delivery. Updates the reporting configuration for this media buy." + }, "push_notification_config": { "$ref": "/schemas/core/push-notification-config.json", - "description": "Optional webhook configuration for async update notifications. Publisher will send webhook when update completes if operation takes longer than immediate response time." + "description": "Optional webhook configuration for async update notifications. Publisher will send webhook when update completes if operation takes longer than immediate response time. This is separate from reporting_webhook which configures ongoing campaign reporting." }, "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 0000000000..48187649fc --- /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 0000000000..d23b0c2bc3 --- /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 0000000000..b1c3185f11 --- /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 0000000000..2b608088db --- /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 0000000000..0e5217d260 --- /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 0000000000..655bbed524 --- /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 0000000000..29e6bc17a7 --- /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 0000000000..0a75a1a21f --- /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 0000000000..a664eb65a9 --- /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 0000000000..d07a0d480d --- /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 0000000000..1fd852a761 --- /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 0000000000..89ba3bfc5c --- /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-lists-request.json b/static/schemas/source/property/list-property-lists-request.json new file mode 100644 index 0000000000..034c38e475 --- /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 0000000000..f80b67ed17 --- /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 0000000000..7b46b096ef --- /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 0000000000..baaf017bb7 --- /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 0000000000..25a43a6439 --- /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 0000000000..c2b70ab712 --- /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 0000000000..6c9939b595 --- /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 0000000000..69b6c8f17f --- /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 0000000000..ec132f2bf3 --- /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 0000000000..53a89a5cf3 --- /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 0000000000..d83cdb49b0 --- /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 0000000000..8258df259c --- /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 0000000000..16597cf433 --- /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 0000000000..be229f4067 --- /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 0000000000..b11db55aef --- /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/static/schemas/source/protocol/get-adcp-capabilities-request.json b/static/schemas/source/protocol/get-adcp-capabilities-request.json new file mode 100644 index 0000000000..7c49d2cd15 --- /dev/null +++ b/static/schemas/source/protocol/get-adcp-capabilities-request.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/protocol/get-adcp-capabilities-request.json", + "title": "Get AdCP Capabilities Request", + "description": "Request payload for get_adcp_capabilities task. Protocol-level capability discovery that works across all AdCP protocols.", + "type": "object", + "properties": { + "protocols": { + "type": "array", + "description": "Specific protocols to query capabilities for. If omitted, returns capabilities for all supported protocols.", + "items": { + "type": "string", + "enum": ["media_buy", "signals"] + } + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "additionalProperties": true +} diff --git a/static/schemas/source/protocol/get-adcp-capabilities-response.json b/static/schemas/source/protocol/get-adcp-capabilities-response.json new file mode 100644 index 0000000000..e64d9e3aee --- /dev/null +++ b/static/schemas/source/protocol/get-adcp-capabilities-response.json @@ -0,0 +1,323 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "/schemas/protocol/get-adcp-capabilities-response.json", + "title": "Get AdCP Capabilities Response", + "description": "Response payload for get_adcp_capabilities task. Protocol-level capability discovery across all AdCP protocols. Each domain protocol has its own capability section.", + "type": "object", + "properties": { + "adcp": { + "type": "object", + "description": "Core AdCP protocol information", + "properties": { + "major_versions": { + "type": "array", + "description": "AdCP major versions supported by this seller. Major versions indicate breaking changes.", + "items": { + "type": "integer", + "minimum": 1 + }, + "minItems": 1 + } + }, + "required": ["major_versions"] + }, + "supported_protocols": { + "type": "array", + "description": "Which AdCP domain protocols this seller supports", + "items": { + "type": "string", + "enum": ["media_buy", "signals", "governance"] + }, + "minItems": 1 + }, + "media_buy": { + "type": "object", + "description": "Media-buy protocol capabilities. Only present if media_buy is in supported_protocols.", + "properties": { + "features": { + "type": "object", + "description": "Optional media-buy features supported. If declared true, the seller MUST honor requests using that feature.", + "properties": { + "inline_creative_management": { + "type": "boolean", + "description": "Supports creatives provided inline in create_media_buy requests" + }, + "property_list_filtering": { + "type": "boolean", + "description": "Honors property_list parameter in get_products to filter results to buyer-approved properties" + }, + "content_standards": { + "type": "boolean", + "description": "Full support for content_standards configuration including sampling rates and category filtering" + } + }, + "additionalProperties": { + "type": "boolean" + } + }, + "execution": { + "type": "object", + "description": "Technical execution capabilities for media buying", + "properties": { + "axe_integrations": { + "type": "array", + "description": "Agentic ad exchange (AXE) integrations supported. URLs are canonical identifiers for exchanges this seller can execute through.", + "items": { + "type": "string", + "format": "uri" + } + }, + "creative_specs": { + "type": "object", + "description": "Creative specification support", + "properties": { + "vast_versions": { + "type": "array", + "description": "VAST versions supported for video creatives", + "items": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+$" + } + }, + "mraid_versions": { + "type": "array", + "description": "MRAID versions supported for rich media mobile creatives", + "items": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+$" + } + }, + "vpaid": { + "type": "boolean", + "description": "VPAID support for interactive video ads" + }, + "simid": { + "type": "boolean", + "description": "SIMID support for interactive video ads" + } + } + }, + "targeting": { + "type": "object", + "description": "Targeting capabilities. If declared true/supported, buyer can use these targeting parameters and seller MUST honor them.", + "properties": { + "geo_countries": { + "type": "boolean", + "description": "Supports country-level geo targeting using ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE')" + }, + "geo_regions": { + "type": "boolean", + "description": "Supports region/state-level geo targeting using ISO 3166-2 subdivision codes (e.g., 'US-NY', 'GB-SCT', 'DE-BY')" + }, + "geo_metros": { + "type": "object", + "description": "Metro area targeting support. Specifies which classification systems are supported.", + "properties": { + "nielsen_dma": { + "type": "boolean", + "description": "Supports Nielsen DMA codes (US market, e.g., '501' for NYC)" + }, + "uk_itl1": { + "type": "boolean", + "description": "Supports UK ITL Level 1 regions" + }, + "uk_itl2": { + "type": "boolean", + "description": "Supports UK ITL Level 2 regions" + }, + "eurostat_nuts2": { + "type": "boolean", + "description": "Supports Eurostat NUTS Level 2 regions (EU)" + } + } + }, + "geo_postal_areas": { + "type": "object", + "description": "Postal area targeting support. Specifies which postal code systems are supported. System names encode country and precision.", + "properties": { + "us_zip": { + "type": "boolean", + "description": "US 5-digit ZIP codes (e.g., '10001')" + }, + "us_zip_plus_four": { + "type": "boolean", + "description": "US 9-digit ZIP+4 codes (e.g., '10001-1234')" + }, + "gb_outward": { + "type": "boolean", + "description": "UK postcode district / outward code (e.g., 'SW1', 'EC1')" + }, + "gb_full": { + "type": "boolean", + "description": "UK full postcode (e.g., 'SW1A 1AA')" + }, + "ca_fsa": { + "type": "boolean", + "description": "Canadian Forward Sortation Area (e.g., 'K1A')" + }, + "ca_full": { + "type": "boolean", + "description": "Canadian full postal code (e.g., 'K1A 0B1')" + }, + "de_plz": { + "type": "boolean", + "description": "German Postleitzahl, 5 digits (e.g., '10115')" + }, + "fr_code_postal": { + "type": "boolean", + "description": "French code postal, 5 digits (e.g., '75001')" + }, + "au_postcode": { + "type": "boolean", + "description": "Australian postcode, 4 digits (e.g., '2000')" + } + } + } + } + } + } + }, + "portfolio": { + "type": "object", + "description": "Information about the seller's media inventory portfolio", + "properties": { + "publisher_domains": { + "type": "array", + "description": "Publisher domains this seller is authorized to represent. Buyers should fetch each publisher's adagents.json for property definitions.", + "items": { + "type": "string", + "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$" + }, + "minItems": 1 + }, + "primary_channels": { + "type": "array", + "description": "Primary advertising channels in this portfolio", + "items": { + "$ref": "/schemas/enums/channels.json" + } + }, + "primary_countries": { + "type": "array", + "description": "Primary countries (ISO 3166-1 alpha-2) where inventory is concentrated", + "items": { + "type": "string", + "pattern": "^[A-Z]{2}$" + } + }, + "description": { + "type": "string", + "description": "Markdown-formatted description of the inventory portfolio", + "maxLength": 5000 + }, + "advertising_policies": { + "type": "string", + "description": "Advertising content policies, restrictions, and guidelines", + "maxLength": 10000 + } + }, + "required": ["publisher_domains"] + } + } + }, + "signals": { + "type": "object", + "description": "Signals protocol capabilities. Only present if signals is in supported_protocols. Reserved for future use.", + "properties": { + "features": { + "type": "object", + "description": "Optional signals features supported", + "additionalProperties": { + "type": "boolean" + } + } + } + }, + "governance": { + "type": "object", + "description": "Governance protocol capabilities. Only present if governance is in supported_protocols. Governance agents provide property data like compliance scores, brand safety ratings, and sustainability metrics.", + "properties": { + "property_features": { + "type": "array", + "description": "Property features this governance agent can evaluate. Each feature describes a score, rating, or certification the agent can provide for properties.", + "items": { + "type": "object", + "properties": { + "feature_id": { + "type": "string", + "description": "Unique identifier for this feature (e.g., 'consent_quality', 'coppa_certified', 'carbon_score')" + }, + "type": { + "type": "string", + "enum": ["binary", "quantitative", "categorical"], + "description": "Data type: 'binary' for yes/no, 'quantitative' for numeric scores, 'categorical' for enum values" + }, + "range": { + "type": "object", + "description": "For quantitative features, the valid range", + "properties": { + "min": { + "type": "number", + "description": "Minimum value" + }, + "max": { + "type": "number", + "description": "Maximum value" + } + }, + "required": ["min", "max"] + }, + "categories": { + "type": "array", + "description": "For categorical features, the valid values", + "items": { + "type": "string" + } + }, + "description": { + "type": "string", + "description": "Human-readable description of what this feature measures" + }, + "methodology_url": { + "type": "string", + "format": "uri", + "description": "URL to documentation explaining how this feature is calculated or measured. Helps buyers understand and compare methodologies across vendors." + } + }, + "required": ["feature_id", "type"] + } + } + } + }, + "extensions_supported": { + "type": "array", + "description": "Extension namespaces this agent supports. Buyers can expect meaningful data in ext.{namespace} fields on responses from this agent. Extension schemas are published in the AdCP extension registry.", + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$", + "description": "Extension namespace (lowercase alphanumeric with underscores, e.g., 'scope3', 'garm', 'iab_tcf')" + }, + "uniqueItems": true + }, + "last_updated": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp of when capabilities were last updated. Buyers can use this for cache invalidation." + }, + "errors": { + "type": "array", + "description": "Task-specific errors and warnings", + "items": { + "$ref": "/schemas/core/error.json" + } + }, + "context": { + "$ref": "/schemas/core/context.json" + }, + "ext": { + "$ref": "/schemas/core/ext.json" + } + }, + "required": ["adcp", "supported_protocols"], + "additionalProperties": true +} diff --git a/static/schemas/source/protocols/adcp-extension.json b/static/schemas/source/protocols/adcp-extension.json index 6e3c070374..ea104fb63c 100644 --- a/static/schemas/source/protocols/adcp-extension.json +++ b/static/schemas/source/protocols/adcp-extension.json @@ -1,14 +1,17 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "/schemas/protocols/adcp-extension.json", - "title": "AdCP Agent Card Extension", - "description": "Extension metadata for agent cards (MCP and A2A) declaring AdCP version and supported protocol domains", + "title": "AdCP Agent Card Extension Params (Deprecated)", + "description": "DEPRECATED: Use tool-based discovery instead. Check for 'get_adcp_capabilities' tool in the agent's tool list, then call it for full capability details. This extension schema is maintained for backward compatibility only.", + "deprecated": true, + "deprecation_note": "AdCP discovery now uses native MCP/A2A tool discovery. If an agent has 'get_adcp_capabilities' tool, it supports AdCP. Call get_adcp_capabilities for version, protocols, features, and capabilities.", "type": "object", "properties": { "adcp_version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$", - "description": "Semantic version of the AdCP specification this agent implements (e.g., '2.4.0')" + "description": "DEPRECATED: Use get_adcp_capabilities response adcp.major_versions instead.", + "deprecated": true }, "protocols_supported": { "type": "array", @@ -22,7 +25,18 @@ }, "minItems": 1, "uniqueItems": true, - "description": "AdCP protocol domains supported by this agent. At least one must be specified." + "description": "DEPRECATED: Use get_adcp_capabilities response supported_protocols instead.", + "deprecated": true + }, + "extensions_supported": { + "type": "array", + "description": "DEPRECATED: Typed extensions can be declared in get_adcp_capabilities ext field.", + "items": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]*$" + }, + "uniqueItems": true, + "deprecated": true } }, "required": [ diff --git a/tests/extension-schemas.test.cjs b/tests/extension-schemas.test.cjs new file mode 100644 index 0000000000..cf27260202 --- /dev/null +++ b/tests/extension-schemas.test.cjs @@ -0,0 +1,445 @@ +#!/usr/bin/env node +/** + * Extension schema validation test suite + * Tests that the typed extension infrastructure is properly configured + */ + +const fs = require('fs'); +const path = require('path'); +const Ajv = require('ajv'); +const addFormats = require('ajv-formats'); + +const SCHEMA_BASE_DIR = path.join(__dirname, '../static/schemas/source'); +const EXTENSIONS_DIR = path.join(SCHEMA_BASE_DIR, 'extensions'); + +// Initialize AJV with formats and custom loader +const ajv = new Ajv({ + allErrors: true, + verbose: true, + strict: false, + loadSchema: loadExternalSchema +}); +addFormats(ajv); + +// Schema loader for resolving $ref +async function loadExternalSchema(uri) { + let relativePath; + if (uri.startsWith('/schemas/v1/')) { + relativePath = uri.replace('/schemas/v1/', ''); + } else if (uri.startsWith('/schemas/')) { + relativePath = uri.replace('/schemas/', ''); + } else { + throw new Error(`Cannot load external schema: ${uri}`); + } + + const schemaPath = path.join(SCHEMA_BASE_DIR, relativePath); + try { + const content = fs.readFileSync(schemaPath, 'utf8'); + return JSON.parse(content); + } catch (error) { + throw new Error(`Failed to load referenced schema ${uri}: ${error.message}`); + } +} + +let totalTests = 0; +let passedTests = 0; +let failedTests = 0; + +function log(message, type = 'info') { + const colors = { + info: '\x1b[0m', + success: '\x1b[32m', + error: '\x1b[31m', + warning: '\x1b[33m' + }; + console.log(`${colors[type]}${message}\x1b[0m`); +} + +async function test(description, testFn) { + totalTests++; + try { + const result = await testFn(); + if (result === true || result === undefined) { + log(`βœ… ${description}`, 'success'); + passedTests++; + } else { + log(`❌ ${description}: ${result}`, 'error'); + failedTests++; + } + } catch (error) { + log(`❌ ${description}: ${error.message}`, 'error'); + if (error.errors) { + console.error('Validation errors:', JSON.stringify(error.errors, null, 2)); + } + failedTests++; + } +} + +// Cache for compiled schemas +const schemaCache = new Map(); + +async function loadAndCompileSchema(schemaPath) { + if (schemaCache.has(schemaPath)) { + return schemaCache.get(schemaPath); + } + + const schemaContent = fs.readFileSync(schemaPath, 'utf8'); + const schema = JSON.parse(schemaContent); + const validate = await ajv.compileAsync(schema); + + schemaCache.set(schemaPath, validate); + return validate; +} + +// Discover extension files (excluding index.json and extension-meta.json) +function discoverExtensionFiles() { + if (!fs.existsSync(EXTENSIONS_DIR)) { + return []; + } + + return fs.readdirSync(EXTENSIONS_DIR) + .filter(file => + file.endsWith('.json') && + file !== 'index.json' && + file !== 'extension-meta.json' + ) + .map(file => path.join(EXTENSIONS_DIR, file)); +} + +async function runTests() { + log('πŸ§ͺ Starting Extension Schema Validation Tests'); + log('=============================================='); + + // Test 1: Extension directory exists + await test('Extension directory exists', async () => { + if (!fs.existsSync(EXTENSIONS_DIR)) { + throw new Error(`Extension directory not found: ${EXTENSIONS_DIR}`); + } + return true; + }); + + // Test 2: Extension index exists and is valid JSON + await test('Extension index exists and is valid JSON', async () => { + const indexPath = path.join(EXTENSIONS_DIR, 'index.json'); + if (!fs.existsSync(indexPath)) { + throw new Error('Extension index.json not found'); + } + const content = fs.readFileSync(indexPath, 'utf8'); + JSON.parse(content); // Will throw if invalid JSON + return true; + }); + + // Test 3: Extension index has proper structure + await test('Extension index has proper structure', async () => { + const indexPath = path.join(EXTENSIONS_DIR, 'index.json'); + const index = JSON.parse(fs.readFileSync(indexPath, 'utf8')); + + if (!index.$schema) { + throw new Error('Extension index missing $schema'); + } + if (!index.$id) { + throw new Error('Extension index missing $id'); + } + if (!index.title) { + throw new Error('Extension index missing title'); + } + if (typeof index.extensions !== 'object') { + throw new Error('Extension index missing extensions object'); + } + if (!index._generated) { + throw new Error('Extension index should have _generated: true marker'); + } + return true; + }); + + // Test 4: Extension meta schema exists and is valid + await test('Extension meta schema exists and is valid', async () => { + const metaPath = path.join(EXTENSIONS_DIR, 'extension-meta.json'); + if (!fs.existsSync(metaPath)) { + throw new Error('Extension extension-meta.json not found'); + } + const content = fs.readFileSync(metaPath, 'utf8'); + const meta = JSON.parse(content); + + // Verify it's a proper schema + if (!meta.$schema) { + throw new Error('Extension meta schema missing $schema'); + } + if (!meta.$id) { + throw new Error('Extension meta schema missing $id'); + } + if (!meta.properties.valid_from) { + throw new Error('Extension meta schema missing valid_from property'); + } + if (!meta.properties.valid_until) { + throw new Error('Extension meta schema missing valid_until property'); + } + return true; + }); + + // Test 5: Agent card extension schema supports extensions_supported field + await test('Agent card extension schema supports extensions_supported', async () => { + const validate = await loadAndCompileSchema( + path.join(SCHEMA_BASE_DIR, 'protocols/adcp-extension.json') + ); + + const validData = { + adcp_version: '2.5.0', + protocols_supported: ['media_buy'], + extensions_supported: ['sustainability', 'custom_vendor'] + }; + + const valid = validate(validData); + if (!valid) { + throw new Error(`Validation failed: ${JSON.stringify(validate.errors)}`); + } + return true; + }); + + // Test 6: Agent card validates without extensions_supported (optional field) + await test('Agent card validates without extensions_supported', async () => { + const validate = await loadAndCompileSchema( + path.join(SCHEMA_BASE_DIR, 'protocols/adcp-extension.json') + ); + + const validData = { + adcp_version: '2.5.0', + protocols_supported: ['media_buy', 'creative'] + }; + + const valid = validate(validData); + if (!valid) { + throw new Error(`Validation failed: ${JSON.stringify(validate.errors)}`); + } + return true; + }); + + // Test 7: Agent card rejects invalid extension namespace format + await test('Agent card rejects invalid extension namespace format', async () => { + const validate = await loadAndCompileSchema( + path.join(SCHEMA_BASE_DIR, 'protocols/adcp-extension.json') + ); + + const invalidData = { + adcp_version: '2.5.0', + protocols_supported: ['media_buy'], + extensions_supported: ['Invalid-Namespace'] // Must be lowercase + }; + + const valid = validate(invalidData); + if (valid) { + throw new Error('Should have rejected invalid namespace format'); + } + return true; + }); + + // Test 8: Product can include arbitrary extension data (untyped) + await test('Product validates with arbitrary extension data', async () => { + const validate = await loadAndCompileSchema( + path.join(SCHEMA_BASE_DIR, 'core/product.json') + ); + + const product = { + product_id: 'test_product', + name: 'Test Product', + description: 'Test description', + publisher_properties: [{ + publisher_domain: 'example.com', + selection_type: 'all' + }], + format_ids: [{ + agent_url: 'https://creative.adcontextprotocol.org', + id: 'display_300x250' + }], + delivery_type: 'guaranteed', + delivery_measurement: { + provider: 'Test Provider' + }, + pricing_options: [{ + is_fixed: true, + pricing_option_id: 'fixed_cpm', + pricing_model: 'cpm', + rate: 10.00, + currency: 'USD' + }], + ext: { + custom_vendor: { + some_field: 'some_value', + nested: { data: true } + }, + another_vendor: { + config: [1, 2, 3] + } + } + }; + + const valid = validate(product); + if (!valid) { + throw new Error(`Validation failed: ${JSON.stringify(validate.errors)}`); + } + return true; + }); + + // Test 9: Extension meta schema validates a sample extension + await test('Extension meta schema validates sample extension structure', async () => { + const validate = await loadAndCompileSchema( + path.join(EXTENSIONS_DIR, 'extension-meta.json') + ); + + const sampleExtension = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: '/schemas/extensions/sustainability.json', + title: 'Sustainability Extension', + description: 'Carbon footprint and green certification data', + valid_from: '2.5', + docs_url: 'https://adcontextprotocol.org/docs/extensions/sustainability', + type: 'object', + properties: { + carbon_kg_per_impression: { type: 'number' }, + certified_green: { type: 'boolean' } + } + }; + + const valid = validate(sampleExtension); + if (!valid) { + throw new Error(`Validation failed: ${JSON.stringify(validate.errors)}`); + } + return true; + }); + + // Test 10: Extension meta schema validates extension with valid_until + await test('Extension meta schema validates extension with valid_until', async () => { + const validate = await loadAndCompileSchema( + path.join(EXTENSIONS_DIR, 'extension-meta.json') + ); + + const sampleExtension = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: '/schemas/extensions/deprecated_feature.json', + title: 'Deprecated Feature Extension', + description: 'A feature that was deprecated in version 3.0', + valid_from: '2.0', + valid_until: '3.0', + type: 'object', + properties: { + legacy_field: { type: 'string' } + } + }; + + const valid = validate(sampleExtension); + if (!valid) { + throw new Error(`Validation failed: ${JSON.stringify(validate.errors)}`); + } + return true; + }); + + // Test 11: Extension meta schema rejects invalid valid_from format + await test('Extension meta schema rejects invalid valid_from format', async () => { + const validate = await loadAndCompileSchema( + path.join(EXTENSIONS_DIR, 'extension-meta.json') + ); + + const invalidExtension = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: '/schemas/extensions/bad_version.json', + title: 'Bad Version Extension', + description: 'Has invalid version format', + valid_from: '2.5.0', // Should be "2.5" not "2.5.0" + type: 'object', + properties: {} + }; + + const valid = validate(invalidExtension); + if (valid) { + throw new Error('Should have rejected invalid valid_from format (semver patch level not allowed)'); + } + return true; + }); + + // Test 12: Extension meta schema rejects invalid $id pattern + await test('Extension meta schema rejects invalid $id pattern', async () => { + const validate = await loadAndCompileSchema( + path.join(EXTENSIONS_DIR, 'extension-meta.json') + ); + + const invalidExtension = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: '/schemas/core/not_an_extension.json', // Wrong path + title: 'Wrong Path Extension', + description: 'Has invalid $id path', + valid_from: '2.5', + type: 'object', + properties: {} + }; + + const valid = validate(invalidExtension); + if (valid) { + throw new Error('Should have rejected invalid $id pattern'); + } + return true; + }); + + // Test 13: Reserved namespaces should be rejected by build script validation + await test('Reserved namespaces are documented', async () => { + // These namespaces are reserved in scripts/build-schemas.cjs + const RESERVED_NAMESPACES = ['adcp', 'core', 'protocol', 'schema', 'meta', 'ext', 'context']; + + // Verify none of the reserved namespaces exist as extension files + for (const reserved of RESERVED_NAMESPACES) { + const reservedPath = path.join(EXTENSIONS_DIR, `${reserved}.json`); + if (fs.existsSync(reservedPath)) { + throw new Error(`Reserved namespace "${reserved}" should not exist as an extension file`); + } + } + return true; + }); + + // Test 14: Discovered extensions validate against meta schema + const extensionFiles = discoverExtensionFiles(); + if (extensionFiles.length > 0) { + for (const extensionPath of extensionFiles) { + const filename = path.basename(extensionPath); + await test(`Extension file ${filename} validates against meta schema`, async () => { + const validate = await loadAndCompileSchema( + path.join(EXTENSIONS_DIR, 'extension-meta.json') + ); + + const content = fs.readFileSync(extensionPath, 'utf8'); + const extension = JSON.parse(content); + + const valid = validate(extension); + if (!valid) { + throw new Error(`Validation failed: ${JSON.stringify(validate.errors)}`); + } + return true; + }); + } + } else { + await test('No extension files to validate (registry is empty)', async () => { + log(' ℹ️ No extension files found - this is expected for initial setup', 'warning'); + return true; + }); + } + + // Summary + log(''); + log('=============================================='); + log(`Tests completed: ${totalTests}`); + log(`βœ… Passed: ${passedTests}`, 'success'); + if (failedTests > 0) { + log(`❌ Failed: ${failedTests}`, 'error'); + log(''); + process.exit(1); + } else { + log(''); + log('πŸŽ‰ All extension schema tests passed!', 'success'); + process.exit(0); + } +} + +// Run tests +runTests().catch(error => { + log(`Fatal error: ${error.message}`, 'error'); + console.error(error); + process.exit(1); +}); diff --git a/tests/schema-validation.test.cjs b/tests/schema-validation.test.cjs index 2d4b0a71c7..40727fc84d 100644 --- a/tests/schema-validation.test.cjs +++ b/tests/schema-validation.test.cjs @@ -130,33 +130,38 @@ function validateSchemaStructure(schemaPath, schema) { function validateCrossReferences(schemas) { const schemaIds = new Set(schemas.map(([_, schema]) => schema.$id)); const missingRefs = []; - + for (const [schemaPath, schema] of schemas) { // Find all $ref occurrences const refs = JSON.stringify(schema).match(/"\$ref":\s*"([^"]+)"/g) || []; - + for (const refMatch of refs) { const ref = refMatch.match(/"\$ref":\s*"([^"]+)"/)[1]; - + // Skip external references (http://, https://) if (ref.startsWith('http://') || ref.startsWith('https://')) { continue; } - + + // Skip internal references (#/$defs/..., #/properties/..., etc.) + if (ref.startsWith('#/')) { + continue; + } + // Check if referenced schema exists if (!schemaIds.has(ref)) { missingRefs.push({ schema: schemaPath, ref }); } } } - + if (missingRefs.length > 0) { - const errorMsg = missingRefs.map(({ schema, ref }) => + const errorMsg = missingRefs.map(({ schema, ref }) => `${path.basename(schema)} -> ${ref}` ).join(', '); return `Missing referenced schemas: ${errorMsg}`; } - + return true; } diff --git a/v2.6-rc/docs/governance/property/tasks/list_property_features.mdx b/v2.6-rc/docs/governance/property/tasks/list_property_features.mdx deleted file mode 100644 index 6c7dcd16a7..0000000000 --- a/v2.6-rc/docs/governance/property/tasks/list_property_features.mdx +++ /dev/null @@ -1,235 +0,0 @@ ---- -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