From b44625aa95d735ccc9be31674cd3c1d5ea51ca5d Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Wed, 29 Apr 2026 19:54:11 -0400 Subject: [PATCH] feat(schemas): release-precision version negotiation (allOf envelope) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pulls in the spec change from adcontextprotocol/adcp#3493 (merged): every AdCP request and response schema now composes shared `adcp_version` and `adcp_major_version` fields via `allOf` `$ref` to a new `core/version-envelope.json` rather than declaring them inline. Three changes here: * Bump `adcp/schemas/VERSION` to `latest` until the next named version is cut (likely `3.1.0-beta.1`). Re-pin before this PR merges. * Teach `adcp/schemas/generate.py` to inline root-level `allOf $ref` envelopes before emitting Go structs. The generator previously read `schema.get('properties')` directly and would have lost every envelope-only field. New `_inline_root_allof` helper merges envelope-referenced `properties` and `required` into the parent schema. Conservative: only handles allOf members that are pure `$ref` to object schemas; anything else is left for the rest of the generator to handle. * Regenerate `adcp/types_gen.go`. 40 request/response structs gain `AdcpVersion string` next to existing `AdcpMajorVersion int`. No hand-written types in `types.go` / `responses.go` / `inputs.go` declared the version fields, so the regen is the full surface. `go test ./...` green; schema drift lint (`python3 lint.py`) clean. Out of scope for this PR (call out for follow-up): * SDK constructor option for setting the pin (e.g. `WithAdcpVersion("3.1")`) with auto-mirror to `AdcpMajorVersion`. * Typed `VersionUnsupportedError` exposing `error.data.supported_versions` per `error-details/version-unsupported.json`. * Helpers for reading the response-echoed `AdcpVersion` and validating against the pinned release. * Wire-shape normalization helper for SDK consumers that key bundles by full semver (`"3.1.0-beta.1"` → `"3.1-beta.1"`). These are tracked under adcontextprotocol/adcp#3570 (Tier 1 epic) for the 3.1 GA milestone. Refs: adcontextprotocol/adcp#3493, adcontextprotocol/adcp#3570 --- adcp/schemas/.bundle-sha256 | 2 +- adcp/schemas/VERSION | 2 +- adcp/schemas/generate.py | 91 ++++- adcp/types_gen.go | 339 ++++++++++++++++--- skills/adcp-brand/SKILL.md | 202 ++++++++++++ skills/adcp-creative/SKILL.md | 301 +++++++++++++++++ skills/adcp-governance/SKILL.md | 566 ++++++++++++++++++++++++++++++++ skills/adcp-media-buy/SKILL.md | 446 +++++++++++++++++++++++++ skills/adcp-si/SKILL.md | 206 ++++++++++++ skills/adcp-signals/SKILL.md | 204 ++++++++++++ skills/call-adcp-agent/SKILL.md | 255 ++++++++++++++ 11 files changed, 2557 insertions(+), 57 deletions(-) create mode 100644 skills/adcp-brand/SKILL.md create mode 100644 skills/adcp-creative/SKILL.md create mode 100644 skills/adcp-governance/SKILL.md create mode 100644 skills/adcp-media-buy/SKILL.md create mode 100644 skills/adcp-si/SKILL.md create mode 100644 skills/adcp-signals/SKILL.md create mode 100644 skills/call-adcp-agent/SKILL.md diff --git a/adcp/schemas/.bundle-sha256 b/adcp/schemas/.bundle-sha256 index 3a664df..f3ba1d7 100644 --- a/adcp/schemas/.bundle-sha256 +++ b/adcp/schemas/.bundle-sha256 @@ -1 +1 @@ -04481e277089bebe551d3dc1294faab56aa876a7c08e0f010a8ca6ba41a06af4 +1f875af6f3eba2c532ef7f18562863c1623f1cc37da8a8fbb13c73fd193df27e diff --git a/adcp/schemas/VERSION b/adcp/schemas/VERSION index 4a36342..a0f9a4b 100644 --- a/adcp/schemas/VERSION +++ b/adcp/schemas/VERSION @@ -1 +1 @@ -3.0.0 +latest diff --git a/adcp/schemas/generate.py b/adcp/schemas/generate.py index 511a39d..c68bfff 100644 --- a/adcp/schemas/generate.py +++ b/adcp/schemas/generate.py @@ -274,10 +274,95 @@ def safe_comment(text, max_len=80): return text.replace('\n', ' ').replace('\r', '')[:max_len] if text else '' def load_schema(path): - """Load a JSON schema file, preserving property order.""" + """Load a JSON schema file, preserving property order, and inline any + root-level ``allOf $ref`` to envelope schemas (e.g. core/version-envelope.json + composed across all request/response schemas). + + JSON Schema draft-07 allOf is used by the protocol to keep envelope-level + fields (adcp_version, adcp_major_version) defined in one place rather than + duplicated across every request/response schema. The Go generator emits + Go structs from ``properties``, so we merge the envelope's properties up + into the parent before the generator iterates. After this step, callers + can treat the schema as if everything were inlined. + """ with open(path) as f: - # Use object_pairs_hook to preserve key order - return json.load(f, object_pairs_hook=OrderedDict) + schema = json.load(f, object_pairs_hook=OrderedDict) + return _inline_root_allof(schema) + + +def _resolve_ref_to_local_path(ref): + """Convert a JSON Schema ``$ref`` ("/schemas//core/x.json" or + "/schemas/core/x.json") into a path relative to cwd (which the generator + already runs from — adcp/schemas/).""" + parts = ref.lstrip('/').split('/') + # Drop "schemas" and an optional version segment ("latest", "3.1.0-beta.0", + # "v3", etc.). What remains is the path under the schemas root. + if parts and parts[0] == 'schemas': + parts = parts[1:] + if parts and (parts[0] == 'latest' or re.match(r'^v?\d', parts[0])): + parts = parts[1:] + return '/'.join(parts) if parts else ref + + +def _inline_root_allof(schema): + """Merge root-level ``allOf $ref`` envelopes into the parent's properties + + required. Conservative: only handles allOf members that are pure ``$ref`` + to object schemas with ``properties``. Anything else is left intact for + the rest of the generator to handle.""" + if not isinstance(schema, dict) or 'allOf' not in schema: + return schema + + merged_props = OrderedDict() + merged_required = [] + remaining_allof = [] + + for member in schema.get('allOf', []): + if isinstance(member, dict) and set(member.keys()) == {'$ref'}: + try: + ref_path = _resolve_ref_to_local_path(member['$ref']) + with open(ref_path) as rf: + referenced = json.load(rf, object_pairs_hook=OrderedDict) + except (OSError, json.JSONDecodeError): + # Ref doesn't resolve. Leave the allOf member in place — the + # generator will fall through to its default $ref handling. + remaining_allof.append(member) + continue + referenced = _inline_root_allof(referenced) + for k, v in referenced.get('properties', {}).items(): + if k not in schema.get('properties', {}) and k not in merged_props: + merged_props[k] = v + for r in referenced.get('required', []) or []: + if r not in merged_required: + merged_required.append(r) + else: + remaining_allof.append(member) + + if merged_props: + # Splice envelope properties before the schema's own properties so + # struct field order reads naturally (adcp_version first, then the + # tool-specific fields). Preserves existing key order otherwise. + existing_props = schema.get('properties', OrderedDict()) + new_props = OrderedDict() + for k, v in merged_props.items(): + new_props[k] = v + for k, v in existing_props.items(): + new_props[k] = v + schema['properties'] = new_props + + if merged_required: + existing_required = list(schema.get('required', []) or []) + for r in merged_required: + if r not in existing_required: + existing_required.append(r) + if existing_required: + schema['required'] = existing_required + + if remaining_allof: + schema['allOf'] = remaining_allof + else: + schema.pop('allOf', None) + + return schema def ref_to_go_name(ref): """Convert a $ref path to a Go type name. diff --git a/adcp/types_gen.go b/adcp/types_gen.go index 3913b3d..5b4204b 100644 --- a/adcp/types_gen.go +++ b/adcp/types_gen.go @@ -1,5 +1,5 @@ // Code generated by generate.py from AdCP JSON schemas. DO NOT EDIT. -// AdCP schema version: 3.0.0 +// AdCP schema version: latest // Source: https://github.com/adcontextprotocol/adcp/tree/main/static/schemas/source package adcp @@ -9,6 +9,15 @@ type AdcpError = map[string]any // --- Enum types --- +// AccountScope — How the seller scoped a billing account relative to the operator and brand dimen +type AccountScope = string +const ( + AccountScopeOperator AccountScope = "operator" + AccountScopeBrand AccountScope = "brand" + AccountScopeOperatorBrand AccountScope = "operator_brand" + AccountScopeAgent AccountScope = "agent" +) + // AccountStatus — Advertiser account status in the account lifecycle type AccountStatus = string const ( @@ -207,6 +216,15 @@ const ( AudienceStatusTooSmall AudienceStatus = "too_small" ) +// AudioChannelLayout — Audio channel configuration for audio and video assets +type AudioChannelLayout = string +const ( + AudioChannelLayoutMono AudioChannelLayout = "mono" + AudioChannelLayoutStereo AudioChannelLayout = "stereo" + AudioChannelLayout51 AudioChannelLayout = "5.1" + AudioChannelLayout71 AudioChannelLayout = "7.1" +) + // AuthScheme — Authentication schemes for push notification endpoints type AuthScheme = string const ( @@ -221,24 +239,47 @@ const ( AvailableMetricSpend AvailableMetric = "spend" AvailableMetricClicks AvailableMetric = "clicks" AvailableMetricCtr AvailableMetric = "ctr" - AvailableMetricVideoCompletions AvailableMetric = "video_completions" + AvailableMetricViews AvailableMetric = "views" + AvailableMetricCompletedViews AvailableMetric = "completed_views" AvailableMetricCompletionRate AvailableMetric = "completion_rate" AvailableMetricConversions AvailableMetric = "conversions" AvailableMetricConversionValue AvailableMetric = "conversion_value" AvailableMetricRoas AvailableMetric = "roas" AvailableMetricCostPerAcquisition AvailableMetric = "cost_per_acquisition" AvailableMetricNewToBrandRate AvailableMetric = "new_to_brand_rate" - AvailableMetricViewability AvailableMetric = "viewability" - AvailableMetricEngagementRate AvailableMetric = "engagement_rate" - AvailableMetricViews AvailableMetric = "views" - AvailableMetricCompletedViews AvailableMetric = "completed_views" AvailableMetricLeads AvailableMetric = "leads" AvailableMetricReach AvailableMetric = "reach" AvailableMetricFrequency AvailableMetric = "frequency" AvailableMetricGrps AvailableMetric = "grps" + AvailableMetricEngagements AvailableMetric = "engagements" + AvailableMetricEngagementRate AvailableMetric = "engagement_rate" + AvailableMetricFollows AvailableMetric = "follows" + AvailableMetricSaves AvailableMetric = "saves" + AvailableMetricProfileVisits AvailableMetric = "profile_visits" + AvailableMetricViewability AvailableMetric = "viewability" AvailableMetricQuartileData AvailableMetric = "quartile_data" AvailableMetricDoohMetrics AvailableMetric = "dooh_metrics" AvailableMetricCostPerClick AvailableMetric = "cost_per_click" + AvailableMetricCostPerCompletedView AvailableMetric = "cost_per_completed_view" + AvailableMetricCPM AvailableMetric = "cpm" + AvailableMetricDownloads AvailableMetric = "downloads" + AvailableMetricUnitsSold AvailableMetric = "units_sold" + AvailableMetricNewToBrandUnits AvailableMetric = "new_to_brand_units" +) + +// BillingParty — Which party the seller invoices for an account. Determines the billing entity an +type BillingParty = string +const ( + BillingPartyOperator BillingParty = "operator" + BillingPartyAgent BillingParty = "agent" + BillingPartyAdvertiser BillingParty = "advertiser" +) + +// BinaryVerdict — Strictly two-outcome evaluation result used for overall record-level verdicts in +type BinaryVerdict = string +const ( + BinaryVerdictPass BinaryVerdict = "pass" + BinaryVerdictFail BinaryVerdict = "fail" ) // BrandAgentType — Functional roles for agents declared in brand.json. Each type represents a disti @@ -350,6 +391,15 @@ const ( CollectionCadenceIrregular CollectionCadence = "irregular" ) +// CollectionKind — What kind of content program a collection represents. Determines how installment +type CollectionKind = string +const ( + CollectionKindSeries CollectionKind = "series" + CollectionKindPublication CollectionKind = "publication" + CollectionKindEventSeries CollectionKind = "event_series" + CollectionKindRotation CollectionKind = "rotation" +) + // CollectionRelationship — How two collections are related. References are scoped to the same get_products type CollectionRelationship = string const ( @@ -713,6 +763,9 @@ const ( ErrorCodeCAMPAIGNSUSPENDED ErrorCode = "CAMPAIGN_SUSPENDED" ErrorCodeGOVERNANCEUNAVAILABLE ErrorCode = "GOVERNANCE_UNAVAILABLE" ErrorCodePERMISSIONDENIED ErrorCode = "PERMISSION_DENIED" + ErrorCodeSCOPEINSUFFICIENT ErrorCode = "SCOPE_INSUFFICIENT" + ErrorCodeREADONLYSCOPE ErrorCode = "READ_ONLY_SCOPE" + ErrorCodeFIELDNOTPERMITTED ErrorCode = "FIELD_NOT_PERMITTED" ) // EscalationSeverity — The severity level of a governance escalation. @@ -764,6 +817,15 @@ const ( ExclusivityExclusive Exclusivity = "exclusive" ) +// FeatureCheckStatus — Per-feature evaluation outcome in content standards checks. For the two-outcome +type FeatureCheckStatus = string +const ( + FeatureCheckStatusPassed FeatureCheckStatus = "passed" + FeatureCheckStatusFailed FeatureCheckStatus = "failed" + FeatureCheckStatusWarning FeatureCheckStatus = "warning" + FeatureCheckStatusUnevaluated FeatureCheckStatus = "unevaluated" +) + // FeedFormat — Catalog feed formats. Determines how the platform parses items from an external type FeedFormat = string const ( @@ -832,6 +894,13 @@ const ( FormatIDParameterDuration FormatIDParameter = "duration" ) +// FrameRateType — Whether the video uses a constant or variable frame rate. Broadcast and SSAI con +type FrameRateType = string +const ( + FrameRateTypeConstant FrameRateType = "constant" + FrameRateTypeVariable FrameRateType = "variable" +) + // FrequencyCapScope — Scope for frequency cap application type FrequencyCapScope = string const ( @@ -861,6 +930,21 @@ const ( GeoLevelPostalArea GeoLevel = "postal_area" ) +// GopType — Group of Pictures structure. SSAI and broadcast require closed GOPs for clean sp +type GopType = string +const ( + GopTypeClosed GopType = "closed" + GopTypeOpen GopType = "open" +) + +// GovernanceDecision — Outcome of a governance check_governance call. Distinct from creative approval s +type GovernanceDecision = string +const ( + GovernanceDecisionApproved GovernanceDecision = "approved" + GovernanceDecisionDenied GovernanceDecision = "denied" + GovernanceDecisionConditions GovernanceDecision = "conditions" +) + // GovernanceDomain — Governance sub-domains that a registry policy applies to. Used to indicate which type GovernanceDomain = string const ( @@ -983,6 +1067,14 @@ const ( MatchIDTypeOther MatchIDType = "other" ) +// MatchType — Keyword targeting match type. broad: ads may serve on queries semantically relat +type MatchType = string +const ( + MatchTypeBroad MatchType = "broad" + MatchTypePhrase MatchType = "phrase" + MatchTypeExact MatchType = "exact" +) + // MediaBuyStatus — Status of a media buy. type MediaBuyStatus = string const ( @@ -995,6 +1087,19 @@ const ( MediaBuyStatusCanceled MediaBuyStatus = "canceled" ) +// MediaBuyValidAction — Actions the buyer can perform on a media buy +type MediaBuyValidAction = string +const ( + MediaBuyValidActionPause MediaBuyValidAction = "pause" + MediaBuyValidActionResume MediaBuyValidAction = "resume" + MediaBuyValidActionCancel MediaBuyValidAction = "cancel" + MediaBuyValidActionUpdateBudget MediaBuyValidAction = "update_budget" + MediaBuyValidActionUpdateDates MediaBuyValidAction = "update_dates" + MediaBuyValidActionUpdatePackages MediaBuyValidAction = "update_packages" + MediaBuyValidActionAddPackages MediaBuyValidAction = "add_packages" + MediaBuyValidActionSyncCreatives MediaBuyValidAction = "sync_creatives" +) + // MetricType — Performance metric types for feedback and optimization type MetricType = string const ( @@ -1018,6 +1123,13 @@ const ( MetroSystemCustom MetroSystem = "custom" ) +// MoovAtomPosition — Position of the moov atom in an MP4 container. 'start' enables progressive downl +type MoovAtomPosition = string +const ( + MoovAtomPositionStart MoovAtomPosition = "start" + MoovAtomPositionEnd MoovAtomPosition = "end" +) + // NotificationType — Type of delivery notification for media buy reporting type NotificationType = string const ( @@ -1043,6 +1155,17 @@ const ( PacingFrontLoaded Pacing = "front_loaded" ) +// PaymentTerms — Standard payment terms for AdCP accounts +type PaymentTerms = string +const ( + PaymentTermsNet15 PaymentTerms = "net_15" + PaymentTermsNet30 PaymentTerms = "net_30" + PaymentTermsNet45 PaymentTerms = "net_45" + PaymentTermsNet60 PaymentTerms = "net_60" + PaymentTermsNet90 PaymentTerms = "net_90" + PaymentTermsPrepay PaymentTerms = "prepay" +) + // PerformanceStandardMetric — Performance metrics that support rate thresholds on media buys. Each metric spec type PerformanceStandardMetric = string const ( @@ -1224,6 +1347,24 @@ const ( RightUseAiGeneratedImage RightUse = "ai_generated_image" ) +// RightsBillingPeriod — Billing period for brand rights pricing +type RightsBillingPeriod = string +const ( + RightsBillingPeriodDaily RightsBillingPeriod = "daily" + RightsBillingPeriodWeekly RightsBillingPeriod = "weekly" + RightsBillingPeriodMonthly RightsBillingPeriod = "monthly" + RightsBillingPeriodQuarterly RightsBillingPeriod = "quarterly" + RightsBillingPeriodAnnual RightsBillingPeriod = "annual" + RightsBillingPeriodOneTime RightsBillingPeriod = "one_time" +) + +// ScanType — Video scan method. Modern digital delivery requires progressive scan; interlaced +type ScanType = string +const ( + ScanTypeProgressive ScanType = "progressive" + ScanTypeInterlaced ScanType = "interlaced" +) + // SiSessionStatus — State of a Sponsored Intelligence session between a host and a brand agent type SiSessionStatus = string const ( @@ -1256,6 +1397,14 @@ const ( SignalValueTypeNumeric SignalValueType = "numeric" ) +// SnapshotUnavailableReason — Machine-readable reason a delivery snapshot was requested but could not be retur +type SnapshotUnavailableReason = string +const ( + SnapshotUnavailableReasonSNAPSHOTUNSUPPORTED SnapshotUnavailableReason = "SNAPSHOT_UNSUPPORTED" + SnapshotUnavailableReasonSNAPSHOTTEMPORARILYUNAVAILABLE SnapshotUnavailableReason = "SNAPSHOT_TEMPORARILY_UNAVAILABLE" + SnapshotUnavailableReasonSNAPSHOTPERMISSIONDENIED SnapshotUnavailableReason = "SNAPSHOT_PERMISSION_DENIED" +) + // SortDirection — Sort direction for list queries type SortDirection = string const ( @@ -1394,6 +1543,13 @@ const ( TransportModePublicTransport TransportMode = "public_transport" ) +// TravelTimeUnit — Time unit for isochrone (travel-time catchment) calculations. +type TravelTimeUnit = string +const ( + TravelTimeUnitMin TravelTimeUnit = "min" + TravelTimeUnitHr TravelTimeUnit = "hr" +) + // UIDType — Type of user identifier. Used in audience sync, event logging, and TMP identity type UIDType = string const ( @@ -1690,6 +1846,8 @@ type Package struct { TargetingOverlay *Targeting `json:"targeting_overlay,omitempty"` MeasurementTerms *MeasurementTerms `json:"measurement_terms,omitempty"` // Agreed billing measurement and makegood terms for this package. Reflects what wa PerformanceStandards []PerformanceStandard `json:"performance_standards,omitempty"` // Agreed performance standards for this package. When any entry specifies a vendor + CommittedMetrics []string `json:"committed_metrics,omitempty"` // Frozen snapshot of the product's `reporting_capabilities.available_metrics` at t + CommittedVendorMetrics []any `json:"committed_vendor_metrics,omitempty"` // Frozen snapshot of the product's `reporting_capabilities.vendor_metrics` at the CreativeAssignments []any `json:"creative_assignments,omitempty"` // Creative assets assigned to this package FormatIDsToProvide []FormatRef `json:"format_ids_to_provide,omitempty"` // Format IDs that creative assets will be provided for this package OptimizationGoals []any `json:"optimization_goals,omitempty"` // Optimization targets for this package. The seller optimizes delivery toward thes @@ -1724,7 +1882,7 @@ type MediaBuyData struct { // CreativeFormat — Represents a creative format with its requirements type CreativeFormat struct { - FormatID FormatRef `json:"format_id"` // Structured format identifier with agent URL and format name + FormatID FormatRef `json:"format_id"` // This format's own identifier — a structured object {agent_url, id}, not a string Name string `json:"name"` // Human-readable format name Description string `json:"description,omitempty"` // Plain text explanation of what this format does and what assets it requires ExampleURL string `json:"example_url,omitempty"` // Optional URL to showcase page with examples and interactive demos of this format @@ -1744,9 +1902,9 @@ type CreativeFormat struct { PricingOptions []VendorPricingOption `json:"pricing_options,omitempty"` // Pricing options for this format. Used by transformation and generation agents th } -// FormatRef — Structured format identifier with agent URL and format name. Can reference: (1) a concrete format wi +// FormatRef — A JSON object — never a plain string — that identifies a creative format by its declaring agent and type FormatRef struct { - AgentURL string `json:"agent_url"` // URL of the agent that defines this format (e.g., 'https://creatives.adcontextpro + AgentURL string `json:"agent_url"` // URL of the agent that defines this format (e.g., 'https://creative.adcontextprot ID string `json:"id"` // Format identifier within the agent's namespace (e.g., 'display_static', 'video_h Width int `json:"width,omitempty"` // Width in pixels for visual formats. When specified, height must also be specifie Height int `json:"height,omitempty"` // Height in pixels for visual formats. When specified, width must also be specifie @@ -1757,7 +1915,7 @@ type FormatRef struct { type CreativeAsset struct { CreativeID string `json:"creative_id"` // Unique identifier for the creative Name string `json:"name"` // Human-readable creative name - FormatID FormatRef `json:"format_id"` // Format identifier specifying which format this creative conforms to. Can be: (1) + FormatID FormatRef `json:"format_id"` // Always a structured object {agent_url, id} — never a plain string. Format identi Assets map[string]any `json:"assets"` // Assets required by the format, keyed by asset_id. Each asset value carries an `a Inputs []any `json:"inputs,omitempty"` // Preview contexts for generative formats - defines what scenarios to generate pre Tags []string `json:"tags,omitempty"` // User-defined tags for organization and searchability @@ -1770,7 +1928,7 @@ type CreativeAsset struct { // CreativeManifest — Complete specification of a creative: format_id + assets. Everything the creative needs — images, te type CreativeManifest struct { - FormatID FormatRef `json:"format_id"` // Format identifier this manifest is for. Can be a template format (id only) or a + FormatID FormatRef `json:"format_id"` // Always a structured object {agent_url, id} — never a plain string. Format identi Assets map[string]any `json:"assets"` // Map of asset IDs to actual asset content. Each key MUST match an asset_id from t Rights []any `json:"rights,omitempty"` // Rights constraints attached to this creative. Each entry represents constraints IndustryIdentifiers []IndustryIdentifier `json:"industry_identifiers,omitempty"` // Industry-standard identifiers for this specific manifest (e.g., Ad-ID, ISCI, Cle @@ -1804,7 +1962,7 @@ type DeliveryTotals struct { ConversionValue float64 `json:"conversion_value,omitempty"` // Total monetary value of attributed conversions (in the reporting currency) Roas float64 `json:"roas,omitempty"` // Return on ad spend (conversion_value / spend) CostPerAcquisition float64 `json:"cost_per_acquisition,omitempty"` // Cost per conversion (spend / conversions) - NewToBrandRate float64 `json:"new_to_brand_rate,omitempty"` // Fraction of conversions from first-time brand buyers (0 = none, 1 = all) + NewToBrandRate float64 `json:"new_to_brand_rate,omitempty"` // Fraction of `conversions` (transactions) from first-time brand buyers, 0 = none, Leads float64 `json:"leads,omitempty"` // Leads generated (convenience alias for by_event_type where event_type='lead') ByEventType []any `json:"by_event_type,omitempty"` // Conversion metrics broken down by event type. Spend-derived metrics (ROAS, CPA) Grps float64 `json:"grps,omitempty"` // Gross Rating Points delivered (for CPP) @@ -1820,7 +1978,13 @@ type DeliveryTotals struct { ProfileVisits float64 `json:"profile_visits,omitempty"` // Visits to the brand's in-platform page (profile, artist page, channel, or storef EngagementRate float64 `json:"engagement_rate,omitempty"` // Platform-specific engagement rate (0.0 to 1.0). Typically engagements/impression CostPerClick float64 `json:"cost_per_click,omitempty"` // Cost per click (spend / clicks) + CostPerCompletedView float64 `json:"cost_per_completed_view,omitempty"` // Cost per completed view (spend / completed_views). Primary CPCV pricing scalar f + CPM float64 `json:"cpm,omitempty"` // Cost per thousand impressions, computed as (spend / impressions) × 1000. Univers + Downloads float64 `json:"downloads,omitempty"` // Audio/podcast downloads (IAB Podcast Measurement Technical Guidelines 2.x method + UnitsSold float64 `json:"units_sold,omitempty"` // Items sold attributed to this delivery. Retail-media scalar distinct from `conve + NewToBrandUnits float64 `json:"new_to_brand_units,omitempty"` // Units sold to first-time brand buyers (count, not rate). Retail-media scalar — t ByActionSource []any `json:"by_action_source,omitempty"` // Conversion metrics broken down by action source (website, app, in_store, etc.). + VendorMetricValues []any `json:"vendor_metric_values,omitempty"` // Reported values for vendor-defined metrics that the product's `reporting_capabil } // Account — A billing account representing the relationship between a buyer and seller. The account determines r @@ -1832,14 +1996,14 @@ type Account struct { Status string `json:"status"` // Account lifecycle status. See the Accounts Protocol overview for the operations Brand *BrandReference `json:"brand,omitempty"` // Brand reference identifying the advertiser Operator string `json:"operator,omitempty"` // Domain of the entity operating this account. When the brand operates directly, t - Billing string `json:"billing,omitempty"` // Who is invoiced on this account. operator: seller invoices the operator (agency + Billing string `json:"billing,omitempty"` // Who is invoiced on this account. See billing_entity for the invoiced party's bus BillingEntity any `json:"billing_entity,omitempty"` // Business entity details for the party responsible for payment. Contains the lega RateCard string `json:"rate_card,omitempty"` // Identifier for the rate card applied to this account PaymentTerms string `json:"payment_terms,omitempty"` // Payment terms agreed for this account. Binding for all invoices when the account CreditLimit any `json:"credit_limit,omitempty"` // Maximum outstanding balance allowed Setup any `json:"setup,omitempty"` // Present when status is 'pending_approval'. Contains next steps for completing ac - AccountScope string `json:"account_scope,omitempty"` // How the seller scoped this account. operator: shared across all brands for this - GovernanceAgents []any `json:"governance_agents,omitempty"` // Governance agent endpoints registered on this account. Authentication credential + AccountScope string `json:"account_scope,omitempty"` + GovernanceAgents []any `json:"governance_agents,omitempty"` // Governance agent endpoint registered on this account. Exactly one entry per sync ReportingBucket any `json:"reporting_bucket,omitempty"` // Cloud storage bucket where the seller delivers offline reporting files for this Sandbox *bool `json:"sandbox,omitempty"` // When true, this is a sandbox account — no real platform calls, no real spend. Fo Ext any `json:"ext,omitempty"` @@ -1951,7 +2115,8 @@ type CreativeBrief struct { // GetAdcpCapabilitiesRequest — Request payload for get_adcp_capabilities task. Protocol-level capability discovery that works acros type GetAdcpCapabilitiesRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. When provided, the selle + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Protocols []string `json:"protocols,omitempty"` // Specific protocols to query capabilities for. If omitted, returns capabilities f Context any `json:"context,omitempty"` Ext any `json:"ext,omitempty"` @@ -1959,6 +2124,8 @@ type GetAdcpCapabilitiesRequest struct { // GetAdcpCapabilitiesResponse — Response payload for get_adcp_capabilities task. Protocol-level capability discovery across all AdCP type GetAdcpCapabilitiesResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Adcp any `json:"adcp"` // Core AdCP protocol information SupportedProtocols []string `json:"supported_protocols"` // AdCP protocols this agent supports. Each value both (a) declares which tools the Account any `json:"account,omitempty"` // Account management capabilities. Describes how accounts are established, what bi @@ -1972,7 +2139,7 @@ type GetAdcpCapabilitiesResponse struct { WebhookSigning any `json:"webhook_signing,omitempty"` // RFC 9421 webhook-signature support for outbound webhook callbacks (top-level pee Identity any `json:"identity,omitempty"` // Operator identity posture — key-scoping and compromise-response controls the age ComplianceTesting any `json:"compliance_testing,omitempty"` // Compliance testing capabilities. The presence of this block declares that the ag - Specialisms []string `json:"specialisms,omitempty"` // Optional — specialized compliance claims this agent supports. Omitting the field + Specialisms []string `json:"specialisms,omitempty"` // Optional — specialized compliance claims this agent supports. Values MUST be keb ExtensionsSupported []string `json:"extensions_supported,omitempty"` // Extension namespaces this agent supports. Buyers can expect meaningful data in e ExperimentalFeatures []string `json:"experimental_features,omitempty"` // Experimental AdCP surfaces this agent implements. A surface is experimental when LastUpdated string `json:"last_updated,omitempty"` // ISO 8601 timestamp of when capabilities were last updated. Buyers can use this f @@ -1983,7 +2150,8 @@ type GetAdcpCapabilitiesResponse struct { // SyncAccountsRequest — Sync advertiser accounts with a seller using upsert semantics. The agent declares which brands it re type SyncAccountsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for at-most-once execution. Natural per-account upse Accounts []AccountInput `json:"accounts"` // Advertiser accounts to sync DeleteMissing *bool `json:"delete_missing,omitempty"` // When true, accounts previously synced by this agent but not included in this req @@ -1993,9 +2161,10 @@ type SyncAccountsRequest struct { Ext any `json:"ext,omitempty"` } -// SyncGovernanceRequest — Sync governance agent endpoints against specific accounts. The seller persists these governance agen +// SyncGovernanceRequest — Sync the governance agent endpoint against specific accounts. The seller persists the governance age type SyncGovernanceRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for at-most-once execution. `account` gives resource Accounts []GovernanceAccountInput `json:"accounts"` // Per-account governance agent configuration. Each entry pairs an account referenc Context any `json:"context,omitempty"` @@ -2021,7 +2190,8 @@ type SyncGovernanceError struct { // ListAccountsRequest — Request parameters for listing accounts accessible to the authenticated agent type ListAccountsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Status string `json:"status,omitempty"` // Filter accounts by status. Omit to return accounts in all statuses. Pagination *PaginationRequest `json:"pagination,omitempty"` Sandbox *bool `json:"sandbox,omitempty"` // Filter by sandbox status. true returns only sandbox accounts, false returns only @@ -2031,7 +2201,9 @@ type ListAccountsRequest struct { // ListAccountsResponse — Response payload for list_accounts task type ListAccountsResponse struct { - Accounts []Account `json:"accounts"` // Array of accounts accessible to the authenticated agent + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con + Accounts []any `json:"accounts"` // Array of accounts accessible to the authenticated agent. Each entry is the full Errors []AdcpError `json:"errors,omitempty"` // Task-specific errors and warnings Pagination *PaginationResponse `json:"pagination,omitempty"` Context any `json:"context,omitempty"` @@ -2040,7 +2212,8 @@ type ListAccountsResponse struct { // GetProductsRequest — Request parameters for discovering or refining advertising products. buying_mode declares the buyer' type GetProductsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con BuyingMode string `json:"buying_mode"` // Declares buyer intent for this request. 'brief': publisher curates product recom Brief string `json:"brief,omitempty"` // Natural language description of campaign requirements. Required when buying_mode Refine []map[string]any `json:"refine,omitempty"` // Array of change requests for iterating on products and proposals from a previous @@ -2060,6 +2233,8 @@ type GetProductsRequest struct { // GetProductsResponse — Response payload for get_products task type GetProductsResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Products []Product `json:"products"` // Array of matching products Proposals []any `json:"proposals,omitempty"` // Optional array of proposed media plans with budget allocations across products. Errors []AdcpError `json:"errors,omitempty"` // Task-specific errors and warnings (e.g., product filtering issues) @@ -2067,6 +2242,7 @@ type GetProductsResponse struct { CatalogApplied *bool `json:"catalog_applied,omitempty"` // Whether the seller filtered results based on the provided catalog. True if the s RefinementApplied []map[string]any `json:"refinement_applied,omitempty"` // Seller's response to each change request in the refine array, matched by positio Incomplete []any `json:"incomplete,omitempty"` // Declares what the seller could not finish within the buyer's time_budget or due + FilterDiagnostics any `json:"filter_diagnostics,omitempty"` // Optional non-fatal diagnostic block describing how the request's `filters` narro Pagination *PaginationResponse `json:"pagination,omitempty"` Sandbox *bool `json:"sandbox,omitempty"` // When true, this response contains simulated data from sandbox mode. Context any `json:"context,omitempty"` @@ -2075,7 +2251,8 @@ type GetProductsResponse struct { // CreateMediaBuyRequest — Request parameters for creating a media buy. Supports two modes: (1) Manual mode - provide packages type CreateMediaBuyRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for this request. If a request with the same idempot PlanID string `json:"plan_id,omitempty"` // Campaign governance plan identifier. Required when the account has governance_ag Account AccountReference `json:"account"` // Account to bill for this media buy. Pass a natural key (brand, operator, optiona @@ -2136,7 +2313,8 @@ type CreateMediaBuySubmitted struct { // GetMediaBuysRequest — Request parameters for retrieving media buy status, creative approval state, and optional delivery s type GetMediaBuysRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Account *AccountReference `json:"account,omitempty"` // Account to retrieve media buys for. When omitted, returns data across all access MediaBuyIDs []string `json:"media_buy_ids,omitempty"` // Array of media buy IDs to retrieve. When omitted, returns a paginated set of acc StatusFilter any `json:"status_filter,omitempty"` // Filter by status. Can be a single status or array of statuses. Defaults to ["act @@ -2149,6 +2327,8 @@ type GetMediaBuysRequest struct { // GetMediaBuysResponse — Response payload for get_media_buys task. Returns media buy configuration, creative approval state, type GetMediaBuysResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con MediaBuys []any `json:"media_buys"` // Array of media buys with status, creative approval state, and optional delivery Errors []AdcpError `json:"errors,omitempty"` // Task-specific errors (e.g., media buy not found) Pagination *PaginationResponse `json:"pagination,omitempty"` // Pagination metadata for the media_buys array. @@ -2159,7 +2339,8 @@ type GetMediaBuysResponse struct { // GetMediaBuyDeliveryRequest — Request parameters for retrieving comprehensive delivery metrics type GetMediaBuyDeliveryRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Account *AccountReference `json:"account,omitempty"` // Filter delivery data to a specific account. When omitted, returns data across al MediaBuyIDs []string `json:"media_buy_ids,omitempty"` // Array of media buy IDs to get delivery data for StatusFilter any `json:"status_filter,omitempty"` // Filter by status. Can be a single status or array of statuses @@ -2174,6 +2355,8 @@ type GetMediaBuyDeliveryRequest struct { // GetMediaBuyDeliveryResponse — Response payload for get_media_buy_delivery task type GetMediaBuyDeliveryResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con NotificationType string `json:"notification_type,omitempty"` // Type of webhook notification (only present in webhook deliveries): scheduled = r PartialData *bool `json:"partial_data,omitempty"` // Indicates if any media buys in this webhook have missing/delayed data (only pres UnavailableCount int `json:"unavailable_count,omitempty"` // Number of media buys with reporting_delayed or failed status (only present in we @@ -2192,7 +2375,8 @@ type GetMediaBuyDeliveryResponse struct { // ListCreativeFormatsRequest — Request parameters for discovering supported creative formats type ListCreativeFormatsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con FormatIDs []FormatRef `json:"format_ids,omitempty"` // Return only these specific format IDs (e.g., from get_products response) AssetTypes []string `json:"asset_types,omitempty"` // Filter to formats that include these asset types. For third-party tags, search f MaxWidth int `json:"max_width,omitempty"` // Maximum width in pixels (inclusive). Returns formats where ANY render has width @@ -2213,6 +2397,8 @@ type ListCreativeFormatsRequest struct { // ListCreativeFormatsResponse — Response payload for list_creative_formats task type ListCreativeFormatsResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Formats []CreativeFormat `json:"formats"` // Full format definitions for all formats this agent supports. Each format's autho CreativeAgents []any `json:"creative_agents,omitempty"` // Optional: Creative agents that provide additional formats. Buyers can recursivel Errors []AdcpError `json:"errors,omitempty"` // Task-specific errors and warnings (e.g., format availability issues) @@ -2224,7 +2410,8 @@ type ListCreativeFormatsResponse struct { // SyncCatalogsRequest — Request parameters for syncing catalog feeds with upsert semantics. Supports bulk operations across type SyncCatalogsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for at-most-once execution. `catalog_id` gives resou Account AccountReference `json:"account"` // Account that owns these catalogs. Catalogs []CatalogInput `json:"catalogs,omitempty"` // Array of catalog feeds to sync (create or update). When omitted, the call is dis @@ -2239,7 +2426,8 @@ type SyncCatalogsRequest struct { // SyncEventSourcesRequest — Request parameters for configuring event sources on an account with upsert semantics. Existing event type SyncEventSourcesRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for at-most-once execution. `event_source_id` gives Account AccountReference `json:"account"` // Account to configure event sources for. EventSources []EventSourceInput `json:"event_sources,omitempty"` // Event sources to sync (create or update). When omitted, the call is discovery-on @@ -2250,7 +2438,8 @@ type SyncEventSourcesRequest struct { // LogEventRequest — Request parameters for logging marketing events type LogEventRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con EventSourceID string `json:"event_source_id"` // Event source configured on the account via sync_event_sources TestEventCode string `json:"test_event_code,omitempty"` // Test event code for validation without affecting production data. Events with th Events []map[string]any `json:"events"` // Events to log @@ -2261,7 +2450,8 @@ type LogEventRequest struct { // ProvidePerformanceFeedbackRequest — Request payload for provide_performance_feedback task type ProvidePerformanceFeedbackRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con MediaBuyID string `json:"media_buy_id"` // Seller's media buy identifier IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for this request. Prevents duplicate feedback submis MeasurementPeriod any `json:"measurement_period"` // Time period for performance measurement @@ -2294,7 +2484,8 @@ type ProvidePerformanceFeedbackError struct { // BuildCreativeRequest — Request to transform, generate, or retrieve a creative manifest. Supports three modes: (1) generatio type BuildCreativeRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Message string `json:"message,omitempty"` // Natural language instructions for the transformation or generation. For pure gen CreativeManifest *CreativeManifest `json:"creative_manifest,omitempty"` // Creative manifest to transform or generate from. For pure generation, this shoul CreativeID string `json:"creative_id,omitempty"` // Reference to a creative in the agent's library. The creative agent resolves this @@ -2319,7 +2510,8 @@ type BuildCreativeRequest struct { // SyncCreativesRequest — Request parameters for syncing creative assets with upsert semantics - supports bulk operations, sco type SyncCreativesRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Account AccountReference `json:"account"` // Account that owns these creatives. Creatives []CreativeInput `json:"creatives"` // Array of creative assets to sync (create or update) CreativeIDs []string `json:"creative_ids,omitempty"` // Optional filter to limit sync scope to specific creative IDs. When provided, onl @@ -2335,7 +2527,8 @@ type SyncCreativesRequest struct { // ListCreativesRequest — Request parameters for querying creative assets from a creative library with filtering, sorting, and type ListCreativesRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Filters *CreativeFilters `json:"filters,omitempty"` Sort any `json:"sort,omitempty"` // Sorting parameters Pagination *PaginationRequest `json:"pagination,omitempty"` @@ -2352,10 +2545,11 @@ type ListCreativesRequest struct { // PreviewCreativeRequest — Request to generate previews of creative manifests. Uses request_type to select single, batch, or va type PreviewCreativeRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con RequestType string `json:"request_type"` // Preview mode. 'single' previews one creative manifest. 'batch' previews multiple CreativeManifest *CreativeManifest `json:"creative_manifest,omitempty"` // Complete creative manifest with all required assets for the format. Required whe - FormatID *FormatRef `json:"format_id,omitempty"` // Format identifier for rendering the preview. Defaults to creative_manifest.forma + FormatID *FormatRef `json:"format_id,omitempty"` // Always a structured object {agent_url, id} — never a plain string. Format identi Inputs []any `json:"inputs,omitempty"` // Array of input sets for generating multiple preview variants. Each input set def TemplateID string `json:"template_id,omitempty"` // Specific template ID for custom format rendering. Used in single mode. Quality string `json:"quality,omitempty"` // Render quality. 'draft' produces fast, lower-fidelity renderings. 'production' p @@ -2370,21 +2564,24 @@ type PreviewCreativeRequest struct { // GetSignalsRequest — Request parameters for discovering and refining signals. Use signal_spec for natural language discov type GetSignalsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Account *AccountReference `json:"account,omitempty"` // Account for this request. When provided, the signals agent returns per-account p SignalSpec string `json:"signal_spec,omitempty"` // Natural language description of the desired signals. When used alone, enables se SignalIDs []SignalID `json:"signal_ids,omitempty"` // Specific signals to look up by data provider and ID. Returns exact matches from Destinations []any `json:"destinations,omitempty"` // Filter signals to those activatable on specific agents/platforms. When omitted, Countries []string `json:"countries,omitempty"` // Countries where signals will be used (ISO 3166-1 alpha-2 codes). When omitted, n Filters *SignalFilters `json:"filters,omitempty"` - MaxResults int `json:"max_results,omitempty"` // Maximum number of results to return - Pagination *PaginationRequest `json:"pagination,omitempty"` + MaxResults int `json:"max_results,omitempty"` // DEPRECATED: Use pagination.max_results instead. When both fields are present, ag + Pagination *PaginationRequest `json:"pagination,omitempty"` // Pagination parameters. Use pagination.max_results (max: 100, default: 50) and pa Context any `json:"context,omitempty"` Ext any `json:"ext,omitempty"` } // GetSignalsResponse — Response payload for get_signals task type GetSignalsResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Signals []any `json:"signals"` // Array of matching signals Errors []AdcpError `json:"errors,omitempty"` // Task-specific errors and warnings (e.g., signal discovery or pricing issues) Pagination *PaginationResponse `json:"pagination,omitempty"` @@ -2395,7 +2592,8 @@ type GetSignalsResponse struct { // ActivateSignalRequest — Request parameters for activating or deactivating a signal on deployment targets type ActivateSignalRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Action string `json:"action,omitempty"` // Whether to activate or deactivate the signal. Deactivating removes the segment f SignalAgentSegmentID string `json:"signal_agent_segment_id"` // The universal identifier for the signal to activate Destinations []DestinationInput `json:"destinations"` // Target destination(s) for activation. If the authenticated caller matches one of @@ -2408,6 +2606,8 @@ type ActivateSignalRequest struct { // ComplyTestControllerRequest — Request payload for the comply_test_controller tool. Triggers seller-side state transitions for comp type ComplyTestControllerRequest struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Scenario string `json:"scenario"` // Test scenario to execute. 'list_scenarios' discovers supported scenarios. 'force Params any `json:"params,omitempty"` // Scenario-specific parameters. Required for all scenarios except list_scenarios. Context any `json:"context,omitempty"` @@ -2445,6 +2645,23 @@ type SimulationSuccess struct { Ext any `json:"ext,omitempty"` } +// ForcedDirectiveSuccess — A force_create_media_buy_arm directive was registered. The directive shapes the next create_media_bu +type ForcedDirectiveSuccess struct { + Success bool `json:"success"` + Forced any `json:"forced"` // Echo of the registered directive. The next create_media_buy call from this sandb + Message string `json:"message,omitempty"` // Human-readable acknowledgement. + Context any `json:"context,omitempty"` + Ext any `json:"ext,omitempty"` +} + +// SeedSuccess — A seed_* scenario successfully pre-populated a fixture in the seller's test state +type SeedSuccess struct { + Success bool `json:"success"` + Message string `json:"message,omitempty"` // Human-readable acknowledgement. + Context any `json:"context,omitempty"` + Ext any `json:"ext,omitempty"` +} + // ControllerError — The scenario failed — invalid transition, unknown entity, unsupported scenario, or invalid params type ControllerError struct { Success bool `json:"success"` @@ -2457,7 +2674,8 @@ type ControllerError struct { // SyncPlansRequest — Push campaign plans to the governance agent. A plan defines the authorized parameters for a campaign type SyncPlansRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for at-most-once execution. `plan_id` gives resource Plans []Plan `json:"plans"` // One or more campaign plans to sync. Context any `json:"context,omitempty"` @@ -2466,6 +2684,8 @@ type SyncPlansRequest struct { // SyncPlansResponse — Response from syncing campaign plans. Returns status and active validation categories for each plan. type SyncPlansResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Plans []any `json:"plans"` // Status for each synced plan. Replayed *bool `json:"replayed,omitempty"` // Set to true when this response is a cached replay returned for an idempotency_ke Context any `json:"context,omitempty"` @@ -2474,7 +2694,8 @@ type SyncPlansResponse struct { // CheckGovernanceRequest — Universal governance check for campaign actions. The governance agent infers the check type from the type CheckGovernanceRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con PlanID string `json:"plan_id"` // Campaign governance plan identifier. The plan uniquely scopes the account and op Caller string `json:"caller"` // URL of the agent making the request. PurchaseType string `json:"purchase_type,omitempty"` // The type of financial commitment being checked. Determines which budget allocati @@ -2492,16 +2713,19 @@ type CheckGovernanceRequest struct { // CheckGovernanceResponse — Governance agent's response to a check request. Returns whether the action is approved under the gov type CheckGovernanceResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con CheckID string `json:"check_id"` // Unique identifier for this governance check record. Use in report_plan_outcome t - Status string `json:"status"` // Governance decision. 'approved': proceed as planned. 'denied': do not proceed. ' + Status string `json:"status"` PlanID string `json:"plan_id"` // Echoed from request. Explanation string `json:"explanation"` // Human-readable explanation of the governance decision. Findings []any `json:"findings,omitempty"` // Specific issues found during the governance check. Present when status is 'denie Conditions []any `json:"conditions,omitempty"` // Present when status is 'conditions'. Specific adjustments the caller must make. ExpiresAt string `json:"expires_at,omitempty"` // When this approval expires. Present when status is 'approved' or 'conditions'. T NextCheck string `json:"next_check,omitempty"` // When the seller should next call check_governance with delivery metrics. Present - CategoriesEvaluated []string `json:"categories_evaluated,omitempty"` // Governance categories evaluated during this check. - PoliciesEvaluated []string `json:"policies_evaluated,omitempty"` // Registry policy IDs evaluated during this check. + CategoriesEvaluated []string `json:"categories_evaluated,omitempty"` // Governance categories evaluated during this check. Each value is an **agent-inte + PoliciesEvaluated []string `json:"policies_evaluated,omitempty"` // Policy IDs evaluated during this check. Includes registry policy IDs (resolved v + Mode string `json:"mode,omitempty"` // Governance enforcement mode active when this check was evaluated. Allows counter GovernanceContext string `json:"governance_context,omitempty"` // Governance context token for this governed action. The buyer MUST attach this to Context any `json:"context,omitempty"` Ext any `json:"ext,omitempty"` @@ -2509,7 +2733,8 @@ type CheckGovernanceResponse struct { // ReportPlanOutcomeRequest — Report the outcome of an action to the governance agent. Called by the orchestrator (buyer-side agen type ReportPlanOutcomeRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con PlanID string `json:"plan_id"` // The plan this outcome is for. The plan uniquely scopes the account and operator; CheckID string `json:"check_id,omitempty"` // The check_id from check_governance. Links the outcome to the governance check th IdempotencyKey string `json:"idempotency_key"` // Client-generated unique key for this request. Prevents duplicate outcome reports @@ -2525,6 +2750,8 @@ type ReportPlanOutcomeRequest struct { // ReportPlanOutcomeResponse — Response from reporting an action outcome. Only returned to the orchestrator (buyer-side agent) that type ReportPlanOutcomeResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con OutcomeID string `json:"outcome_id"` // Unique identifier for this outcome record. Status string `json:"status"` // 'accepted' means state updated with no issues. 'findings' means issues were dete CommittedBudget float64 `json:"committed_budget,omitempty"` // Budget committed from this outcome. Present for 'completed' and 'failed' outcome @@ -2537,7 +2764,8 @@ type ReportPlanOutcomeResponse struct { // GetPlanAuditLogsRequest — Retrieve governance state and audit trail for one or more plans. type GetPlanAuditLogsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con PlanIDs []string `json:"plan_ids,omitempty"` // Plan IDs to retrieve. For a single plan, pass a one-element array. Plans uniquel PortfolioPlanIDs []string `json:"portfolio_plan_ids,omitempty"` // Portfolio plan IDs. The governance agent expands each to its member_plan_ids and GovernanceContexts []string `json:"governance_contexts,omitempty"` // Filter audit entries by governance context. Returns only checks and outcomes tha @@ -2549,6 +2777,8 @@ type GetPlanAuditLogsRequest struct { // GetPlanAuditLogsResponse — Governance state and audit trail for one or more plans. type GetPlanAuditLogsResponse struct { + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Plans []any `json:"plans"` // Audit data for each requested plan. Context any `json:"context,omitempty"` Ext any `json:"ext,omitempty"` @@ -2556,7 +2786,8 @@ type GetPlanAuditLogsResponse struct { // CreateCollectionListRequest — Request parameters for creating a new collection list type CreateCollectionListRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Account *AccountReference `json:"account,omitempty"` // Account that will own the list. Pass a natural key (brand, operator, optional sa Name string `json:"name"` // Human-readable name for the list Description string `json:"description,omitempty"` // Description of the list's purpose @@ -2570,7 +2801,8 @@ type CreateCollectionListRequest struct { // GetCollectionListRequest — Request parameters for retrieving a collection list with resolved collections type GetCollectionListRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con ListID string `json:"list_id"` // ID of the collection list to retrieve Account *AccountReference `json:"account,omitempty"` // Account that owns the list. Required when the authenticated agent has access to Resolve *bool `json:"resolve,omitempty"` // Whether to apply filters and return resolved collections (default: true) @@ -2581,7 +2813,8 @@ type GetCollectionListRequest struct { // UpdateCollectionListRequest — Request parameters for updating an existing collection list type UpdateCollectionListRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con ListID string `json:"list_id"` // ID of the collection list to update Account *AccountReference `json:"account,omitempty"` // Account that owns the list. Required when the authenticated agent has access to Name string `json:"name,omitempty"` // New name for the list @@ -2597,7 +2830,8 @@ type UpdateCollectionListRequest struct { // DeleteCollectionListRequest — Request parameters for deleting a collection list type DeleteCollectionListRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con ListID string `json:"list_id"` // ID of the collection list to delete Account *AccountReference `json:"account,omitempty"` // Account that owns the list. Required when the authenticated agent has access to Context any `json:"context,omitempty"` @@ -2607,7 +2841,8 @@ type DeleteCollectionListRequest struct { // ListCollectionListsRequest — Request parameters for listing collection lists type ListCollectionListsRequest struct { - AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against + AdcpVersion string `json:"adcp_version,omitempty"` // Release-precision AdCP version (VERSION.RELEASE, e.g. "3.0", "3.1", "3.1-beta"). + AdcpMajorVersion int `json:"adcp_major_version,omitempty"` // DEPRECATED in favor of adcp_version (release-precision string). Servers MUST con Account *AccountReference `json:"account,omitempty"` // Filter to lists owned by this account. When omitted, returns lists across all ac NameContains string `json:"name_contains,omitempty"` // Filter to lists whose name contains this string Pagination *PaginationRequest `json:"pagination,omitempty"` diff --git a/skills/adcp-brand/SKILL.md b/skills/adcp-brand/SKILL.md new file mode 100644 index 0000000..8620108 --- /dev/null +++ b/skills/adcp-brand/SKILL.md @@ -0,0 +1,202 @@ +--- +name: adcp-brand +description: Execute AdCP Brand Protocol operations with brand agents - get brand identity data, search for licensable rights, acquire rights for campaigns, and manage existing grants. Use when users want to look up brand identities, find talent or IP for licensing, or manage rights grants. +--- + +# AdCP Brand Protocol + +This skill enables you to execute the AdCP Brand Protocol with brand agents. The Brand Protocol provides access to brand identity, creative guidelines, and licensable rights (talent, IP, content). + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Brand Protocol provides 4 standardized tasks: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_brand_identity` | Get brand identity and guidelines | ~1-3s | +| `get_rights` | Search licensable rights | ~1-5s | +| `acquire_rights` | Acquire rights for a campaign | ~1-10s | +| `update_rights` | Modify an existing grant | ~1-5s | + +## Typical Workflow + +### Brand Identity Lookup +1. **Get identity**: `get_brand_identity` with brand domain and optional field filter +2. **Use data**: Apply colors, logos, tone, guidelines to creative generation + +### Rights Licensing +1. **Search rights**: `get_rights` with natural language query and use types +2. **Review options**: Evaluate matches by pricing, availability, compatibility +3. **Acquire**: `acquire_rights` with selected pricing option and campaign details +4. **Manage**: `update_rights` to extend, adjust caps, or pause/resume + +--- + +## Task Reference + +### get_brand_identity + +Get brand identity data from a brand agent. + +**Request:** +```json +{ + "brand_id": "athlete-jane-doe", + "fields": ["description", "logos", "colors", "tone"], + "use_case": "creative_production", + "authorized": true +} +``` + +**Key fields:** +- `brand_id` (string, required): Brand identifier within the agent's roster +- `fields` (array, optional): Sections to include — `description`, `industry`, `keller_type`, `logos`, `colors`, `fonts`, `visual_guidelines`, `tone`, `tagline`, `voice_synthesis`, `assets`, `rights`. Omit for all. +- `use_case` (string, optional): Intended use — `endorsement`, `voice_synthesis`, `likeness`, `creative_production`, `media_planning` +- `authorized` (boolean, optional): Sandbox only — simulate authorized access to see protected fields. Real agents use OAuth. Default false. + +**Response contains:** +- `brand`: Brand identity object with requested fields +- Public fields (always available): `description`, `industry`, `logos` (public subset) +- Protected fields (require authorization): `colors`, `fonts`, `tone`, `voice_synthesis`, `visual_guidelines`, full `assets` + +--- + +### get_rights + +Search for licensable rights (talent, IP, content) from a brand agent. + +**Request:** +```json +{ + "query": "Dutch athlete for restaurant brand in Amsterdam, budget 400 EUR/month", + "uses": ["likeness", "endorsement"], + "buyer_brand": { + "domain": "restaurant.nl" + }, + "countries": ["NL"], + "include_excluded": false +} +``` + +**Key fields:** +- `query` (string, required): Natural language description of desired rights +- `uses` (array, required): Rights uses — `likeness`, `voice`, `name`, `endorsement` +- `buyer_brand` (object, optional): Buyer brand for compatibility filtering — `{ domain, brand_id }` +- `countries` (array, optional): Countries where rights are needed (ISO 3166-1 alpha-2) +- `brand_id` (string, optional): Search within a specific brand only +- `include_excluded` (boolean, optional): Include filtered-out results with reasons. Default false. + +**Response contains:** +- `rights`: Array of matching rights offerings with: + - `rights_id`: Use in `acquire_rights` + - `brand_id`, `name`, `description`: Who/what the rights cover + - `uses`: Available use types + - `pricing_options`: Array with `pricing_option_id`, `price`, `currency`, `period` + - `availability`: Geographic and temporal restrictions + - `exclusions`: Any brand/category conflicts + +--- + +### acquire_rights + +Acquire rights from a brand agent for a campaign. + +**Request:** +```json +{ + "rights_id": "rights_jane_doe_endorsement", + "pricing_option_id": "monthly_standard", + "buyer": { + "domain": "restaurant.nl" + }, + "campaign": { + "description": "Social media campaign featuring athlete endorsement for Amsterdam restaurant launch", + "uses": ["likeness", "endorsement"], + "countries": ["NL"], + "estimated_impressions": 500000, + "start_date": "2025-03-01", + "end_date": "2025-06-30" + } +} +``` + +**Key fields:** +- `rights_id` (string, required): From `get_rights` response +- `pricing_option_id` (string, required): Selected pricing option +- `buyer` (object, required): Buyer brand identity — `{ domain, brand_id }` +- `campaign` (object, required): Campaign details for rights clearance + - `description` (string, required): How the rights will be used + - `uses` (array, required): Rights uses for this campaign + - `countries` (array, optional): Campaign countries + - `estimated_impressions` (integer, optional): Estimated total impressions + - `start_date`, `end_date` (string, optional): Campaign dates (YYYY-MM-DD) + +**Response contains:** +- `status`: `acquired`, `pending_approval`, or `rejected` +- `rights_grant_id`: Grant identifier (if acquired) +- `generation_credentials`: Credentials for AI generation (voice synthesis, likeness, etc.) +- `rejection_reason`: Why the request was rejected (category conflict, exclusivity, etc.) + +--- + +### update_rights + +Update an existing rights grant — extend dates, adjust impression caps, or pause/resume. + +**Request:** +```json +{ + "rights_id": "grant_abc123", + "end_date": "2025-09-30", + "impression_cap": 1000000, + "paused": false +} +``` + +**Key fields:** +- `rights_id` (string, required): Rights grant identifier from `acquire_rights` +- `end_date` (string, optional): New end date (must be >= current end date) +- `impression_cap` (number, optional): New impression cap (must be >= current) +- `paused` (boolean, optional): Pause or resume the grant + +--- + +## Key Concepts + +### Public vs Protected Fields + +Brand agents distinguish between public and protected data: +- **Public**: Available without authorization — basic description, industry, public logos +- **Protected**: Requires OAuth or authorized flag — colors, fonts, tone, voice synthesis credentials, full asset library + +### Rights Use Types + +- `likeness`: Use of a person's visual likeness (photos, AI-generated images) +- `voice`: Voice synthesis or audio recording rights +- `name`: Use of a person's name in advertising +- `endorsement`: Endorsement/testimonial rights + +### Rights Clearance + +`acquire_rights` checks: +1. Brand/category compatibility (no competitor conflicts) +2. Geographic availability +3. Temporal availability +4. Existing exclusivity agreements + +Results: `acquired` (immediate), `pending_approval` (human review), or `rejected` (with reason). + +--- + +## Error Handling + +Common error codes: + +- `BRAND_NOT_FOUND`: Invalid brand_id +- `RIGHTS_NOT_FOUND`: Invalid rights_id +- `PRICING_OPTION_NOT_FOUND`: Invalid pricing_option_id +- `CATEGORY_CONFLICT`: Buyer brand conflicts with existing agreements +- `GEOGRAPHIC_RESTRICTION`: Rights not available in requested countries +- `AUTHORIZATION_REQUIRED`: Protected fields require OAuth diff --git a/skills/adcp-creative/SKILL.md b/skills/adcp-creative/SKILL.md new file mode 100644 index 0000000..59d7077 --- /dev/null +++ b/skills/adcp-creative/SKILL.md @@ -0,0 +1,301 @@ +--- +name: adcp-creative +description: Execute AdCP Creative Protocol operations with creative agents - build creatives from briefs or existing assets, preview renderings, and discover format specifications. Use when users want to generate or transform ad creatives, preview how ads will look, or understand creative format requirements. +--- + +# AdCP Creative Protocol + +This skill enables you to execute the AdCP Creative Protocol with creative agents. Use the standard MCP tools (`list_creative_formats`, `build_creative`, `preview_creative`) exposed by the connected agent. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Creative Protocol provides 4 standardized tasks for building and previewing advertising creatives: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `list_creative_formats` | View format specifications | ~1s | +| `build_creative` | Generate or transform creatives | ~30s-5m | +| `preview_creative` | Get visual previews | ~5s | +| `get_creative_delivery` | Variant-level delivery data | ~5-30s | + +## Typical Workflow + +1. **Discover formats**: `list_creative_formats` to see available format specs +2. **Build creative**: `build_creative` to generate or transform a manifest +3. **Preview**: `preview_creative` to see how it renders +4. **Sync**: Use `sync_creatives` (media-buy task) to traffic the creative + +--- + +## Task Reference + +### list_creative_formats + +Discover creative formats and their specifications. + +**Request:** +```json +{ + "type": "video", + "asset_types": ["image", "text"] +} +``` + +**Key fields:** +- `format_ids` (array, optional): Request specific format IDs +- `type` (string, optional): Filter by type: `video`, `display`, `audio`, `dooh` +- `asset_types` (array, optional): Filter by accepted asset types +- `max_width`, `max_height` (integer, optional): Dimension constraints +- `is_responsive` (boolean, optional): Filter for responsive formats +- `name_search` (string, optional): Search formats by name + +**Response contains:** +- `formats`: Array of format definitions with `format_id`, `name`, `type`, `assets_required`, `renders` +- `creative_agents`: Optional array of other creative agents providing additional formats + +--- + +### build_creative + +Generate a creative from scratch or transform an existing creative to a different format. + +**Pure Generation (from brief):** +```json +{ + "message": "Create a banner promoting our winter sale with a warm, inviting feel", + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250_generative" + }, + "brand": { + "domain": "mybrand.com" + } +} +``` + +**Transformation (resize/reformat):** +```json +{ + "message": "Adapt this leaderboard to a 300x250 banner", + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_728x90" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.mybrand.com/leaderboard.png", + "width": 728, + "height": 90 + }, + "headline": { + "asset_type": "text", + "content": "Spring Sale - 30% Off" + } + } + }, + "target_format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +**Key fields:** +- `message` (string, optional): Natural language instructions for generation/transformation +- `creative_manifest` (object, optional): Source manifest - minimal for generation, complete for transformation +- `target_format_id` (object, required): Format to generate - `{ agent_url, id }` + +**Response contains:** +- `creative_manifest`: Complete manifest ready for `preview_creative` or `sync_creatives` + +--- + +### preview_creative + +Generate visual previews of creative manifests. + +**Single preview:** +```json +{ + "request_type": "single", + "creative_manifest": { + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + } + } + } +} +``` + +**With device variants:** +```json +{ + "request_type": "single", + "creative_manifest": { /* includes format_id, assets */ }, + "inputs": [ + { "name": "Desktop", "macros": { "DEVICE_TYPE": "desktop" } }, + { "name": "Mobile", "macros": { "DEVICE_TYPE": "mobile" } } + ] +} +``` + +**Batch preview (5-10x faster):** +```json +{ + "request_type": "batch", + "requests": [ + { "creative_manifest": { /* creative 1 */ } }, + { "creative_manifest": { /* creative 2 */ } } + ] +} +``` + +**Key fields:** +- `request_type` (string, required): `"single"` or `"batch"` +- `format_id` (object, optional): Format identifier. Defaults to `creative_manifest.format_id` if omitted. +- `creative_manifest` (object, required): Complete creative manifest +- `inputs` (array, optional): Generate variants with different macros/contexts +- `output_format` (string, optional): `"url"` (default) or `"html"` + +**Response contains:** +- `previews`: Array of preview objects with `preview_url` or `preview_html` +- `expires_at`: When preview URLs expire + +--- + +### get_creative_delivery + +Retrieve variant-level creative delivery data from a creative agent. Returns what was generated, served, and how each variant performed. + +**Request:** +```json +{ + "media_buy_ids": ["mb_abc123"], + "start_date": "2025-01-01", + "end_date": "2025-01-31", + "max_variants": 10 +} +``` + +**Key fields:** +- `media_buy_ids` (array, optional): Filter to specific media buys +- `creative_ids` (array, optional): Filter to specific creatives +- `start_date`, `end_date` (string, optional): Delivery period (YYYY-MM-DD) +- `max_variants` (integer, optional): Max variants per creative (useful for generative creatives) +- `account` (object, optional): Account for routing and scoping + +At least one scoping filter (`media_buy_ids` or `creative_ids`) is required. + +**Response contains:** +- `creatives`: Array with variant-level delivery data + - `variant_id`: Unique variant identifier (use in `preview_creative` with `request_type: "variant"`) + - `generation_context`: What triggered this variant (page topic, device, etc.) + - `delivery_metrics`: Impressions, clicks, completions + - `ext`: Platform engagement metrics (likes, shares, comments) + +--- + +## Key Concepts + +### Format IDs + +All format references use structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies the creative agent authoritative for this format. + +### Creative Manifests + +Manifests pair format specifications with actual assets: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + }, + "assets": { + "banner_image": { + "asset_type": "image", + "url": "https://cdn.example.com/banner.png", + "width": 300, + "height": 250 + }, + "headline": { + "asset_type": "text", + "content": "Shop Now" + }, + "clickthrough_url": { + "asset_type": "url", + "url": "https://brand.com/sale" + } + } +} +``` + +### Asset Types + +Common asset types: +- `image`: Static images (JPEG, PNG, WebP) +- `video`: Video files (MP4, WebM) or VAST tags +- `audio`: Audio files (MP3, M4A) or DAAST tags +- `text`: Headlines, descriptions, CTAs +- `html`: HTML5 creatives or third-party tags +- `javascript`: JavaScript tags +- `url`: Tracking pixels, clickthrough URLs + +### Brand identity + +For generative creatives, provide brand context by domain: +```json +{ + "brand": { + "domain": "acmecorp.com" + } +} +``` + +The agent resolves the domain to retrieve the brand's identity (name, colors, guidelines, etc.) from its `brand.json` file. + +### Generative vs Transformation + +- **Pure Generation**: Provide `target_format_id`, `brand`, and a natural language `message`. Creative agent generates all output assets from scratch. +- **Transformation**: Complete manifest with existing assets. Creative agent adapts to target format, following `message` guidance. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid manifest or format_id +- **404 Not Found**: Format not supported by this agent +- **422 Validation Error**: Manifest doesn't match format requirements + +Error responses include: +```json +{ + "error": { + "code": "INVALID_FORMAT_ID", + "message": "format_id must be a structured object with 'agent_url' and 'id' fields" + } +} +``` diff --git a/skills/adcp-governance/SKILL.md b/skills/adcp-governance/SKILL.md new file mode 100644 index 0000000..ad60b77 --- /dev/null +++ b/skills/adcp-governance/SKILL.md @@ -0,0 +1,566 @@ +--- +name: adcp-governance +description: Execute AdCP Governance Protocol operations with governance agents - manage property lists, collection lists, content standards, and campaign governance (plans, checks, outcomes, audit trail). Use when users want to create include/exclude lists, set up brand safety rules, validate content delivery, register campaign plans, validate actions against policy, or produce internal/shareable audit trails. +--- + +# AdCP Governance Protocol + +This skill enables you to execute the AdCP Governance Protocol with governance agents. Covers four areas: property lists (site-level targeting), collection lists (program-level targeting), content standards (brand safety rules), and campaign governance (plans, checks, outcomes, audit trail). + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Governance Protocol provides 21 standardized tasks across four areas: + +### Property Lists +| Task | Purpose | Response Time | +|------|---------|---------------| +| `create_property_list` | Create include/exclude list | ~1s | +| `update_property_list` | Modify list filters/properties | ~1s | +| `get_property_list` | Retrieve list with optional resolution | ~1-5s | +| `list_property_lists` | List all accessible lists | ~1s | +| `delete_property_list` | Delete a list | ~1s | + +### Collection Lists +| Task | Purpose | Response Time | +|------|---------|---------------| +| `create_collection_list` | Create program-level list | ~1s | +| `update_collection_list` | Modify list | ~1s | +| `get_collection_list` | Retrieve with optional resolution | ~1-5s | +| `list_collection_lists` | List all accessible lists | ~1s | +| `delete_collection_list` | Delete a list | ~1s | + +### Content Standards +| Task | Purpose | Response Time | +|------|---------|---------------| +| `create_content_standards` | Create brand safety rules | ~1s | +| `get_content_standards` | Retrieve standards by ID | ~1s | +| `update_content_standards` | Modify rules | ~1s | +| `list_content_standards` | List all accessible standards | ~1s | +| `calibrate_content` | Test content against standards | ~5-30s | +| `get_media_buy_artifacts` | Get creatives for compliance review | ~5s | +| `validate_content_delivery` | Audit delivery compliance | ~10-60s | + +### Campaign Governance +| Task | Purpose | Response Time | +|------|---------|---------------| +| `sync_plans` | Push or update a campaign plan with budget authority and policies | ~1s | +| `check_governance` | Validate an action (intent or execution) against the plan | ~1-5s | +| `report_plan_outcome` | Report a completed action so plan budget state advances | ~1s | +| `get_plan_audit_logs` | Retrieve governance state, budget tracking, and audit trail | ~1-5s | + +> **Experimental in 3.0.** Campaign governance may change between 3.x releases with at least 6 weeks' notice. Sellers MUST declare `governance.campaign` in `experimental_features` to participate. See [experimental status](/docs/reference/experimental-status). + +## Typical Workflow + +### Property Lists (site-level) +1. **Create list**: `create_property_list` with base properties and filters +2. **Resolve**: `get_property_list` with `resolve: true` to see matched properties +3. **Refine**: `update_property_list` to adjust filters +4. **Apply**: Reference `list_id` in `create_media_buy` targeting + +### Collection Lists (program-level, CTV) +1. **Create list**: `create_collection_list` with distribution IDs or genre filters +2. **Resolve**: `get_collection_list` with `resolve: true` to see matched programs +3. **Apply**: Reference in campaign targeting for CTV brand safety + +### Content Standards +1. **Create standards**: `create_content_standards` with rules +2. **Calibrate**: `calibrate_content` with test samples to validate configuration +3. **Monitor**: `get_media_buy_artifacts` + `validate_content_delivery` for ongoing compliance + +### Campaign Governance +1. **Sync governance agents** to seller accounts via `sync_governance` (lives in the Accounts protocol) +2. **Register the plan**: `sync_plans` with budget authority, channel allocation, and resolved policy IDs +3. **Validate actions**: `check_governance` on intent (pre-discovery) and execution (pre-buy). Returns an opaque `governance_context` token the seller echoes on subsequent checks. +4. **Report outcomes**: `report_plan_outcome` so committed/remaining budget advances +5. **Audit and produce shareable views**: `get_plan_audit_logs` filtered by `governance_contexts` for the requesting party. See [audit trail: internal vs shareable views](/docs/governance/campaign/audit-trail). + +--- + +## Task Reference + +### create_property_list + +Create a property list for brand safety and inventory targeting. + +**Request:** +```json +{ + "name": "Premium News Properties", + "description": "Tier 1 news publishers for brand campaigns", + "base_properties": [ + { + "selection_type": "publisher_tags", + "publisher_domain": "publisher.com", + "tags": ["premium", "news"] + } + ], + "filters": { + "countries_all": ["US", "GB"], + "channels_any": ["display", "video"] + }, + "brand": { + "domain": "acmecorp.com" + } +} +``` + +**Key fields:** +- `name` (string, required): Human-readable name +- `description` (string, optional): Purpose of the list +- `base_properties` (array, optional): Property sources — `publisher_tags`, `publisher_ids`, or `identifiers` +- `filters` (object, optional): Resolution filters — `countries_all`, `channels_any`, `property_types`, `feature_requirements`, `exclude_identifiers` +- `brand` (object, optional): Brand reference for automatic rule inference + +--- + +### update_property_list + +Modify an existing property list. + +**Request:** +```json +{ + "list_id": "pl_abc123", + "filters": { + "countries_all": ["US", "GB", "DE"], + "channels_any": ["display", "video", "ctv"] + } +} +``` + +**Key fields:** +- `list_id` (string, required): Property list identifier +- `name`, `description` (string, optional): Update metadata +- `base_properties` (array, optional): Replace property sources +- `filters` (object, optional): Replace filter configuration + +--- + +### get_property_list + +Retrieve a property list with optional resolution. + +**Request:** +```json +{ + "list_id": "pl_abc123", + "resolve": true, + "max_results": 50 +} +``` + +**Key fields:** +- `list_id` (string, required): Property list identifier +- `resolve` (boolean, optional): Resolve filters and return property identifiers (default: false) +- `max_results` (number, optional): Max properties when resolved + +--- + +### list_property_lists + +List all property lists accessible to the authenticated principal. + +**Request:** +```json +{ + "name_contains": "premium" +} +``` + +**Key fields:** +- `name_contains` (string, optional): Filter by name substring +- `max_results` (number, optional): Max results + +--- + +### delete_property_list + +Delete a property list. + +**Request:** +```json +{ + "list_id": "pl_abc123" +} +``` + +**Key fields:** +- `list_id` (string, required): Property list identifier to delete + +--- + +### create_collection_list + +Create a collection list for program-level brand safety (CTV, podcast, streaming). + +**Request:** +```json +{ + "name": "Family-Safe CTV Programs", + "description": "Programs suitable for family brand campaigns", + "base_collections": [ + { + "selection_type": "publisher_genres", + "publisher_domain": "ctv-publisher.com", + "genres": ["family", "comedy"], + "genre_taxonomy": "iab_content_taxonomy_3.0" + } + ], + "filters": { + "content_ratings_exclude": [ + { "system": "us_tv", "rating": "TV-MA" } + ], + "kinds": ["series"] + }, + "brand": { + "domain": "familybrand.com" + } +} +``` + +**Key fields:** +- `name` (string, required): Human-readable name +- `base_collections` (array, optional): Collection sources — `distribution_ids`, `publisher_collections`, or `publisher_genres` +- `filters` (object, optional): `content_ratings_exclude`, `content_ratings_include`, `genres_exclude`, `genres_include`, `kinds`, `production_quality` +- `brand` (object, optional): Brand reference + +**Distribution identifier types:** `imdb_id`, `gracenote_id`, `eidr_id` + +--- + +### update_collection_list + +Modify an existing collection list. + +**Request:** +```json +{ + "list_id": "cl_abc123", + "filters": { + "content_ratings_exclude": [ + { "system": "us_tv", "rating": "TV-MA" }, + { "system": "us_tv", "rating": "TV-14" } + ] + } +} +``` + +**Key fields:** +- `list_id` (string, required): Collection list identifier +- `base_collections`, `filters` (optional): Replace configuration + +--- + +### get_collection_list + +Retrieve a collection list with optional resolution. + +**Request:** +```json +{ + "list_id": "cl_abc123", + "resolve": true +} +``` + +**Key fields:** +- `list_id` (string, required): Collection list identifier +- `resolve` (boolean, optional): Resolve and return collection entries +- `max_results` (number, optional): Max collections when resolved + +--- + +### list_collection_lists + +List all collection lists accessible to the authenticated principal. + +**Request:** +```json +{ + "name_contains": "family" +} +``` + +--- + +### delete_collection_list + +Delete a collection list. + +**Request:** +```json +{ + "list_id": "cl_abc123" +} +``` + +--- + +### create_content_standards + +Create content standards (brand safety rules) for campaign compliance. + +**Request:** +```json +{ + "name": "Automotive Brand Safety", + "description": "Content rules for automotive brand campaigns", + "rules": [ + { "rule_type": "category", "action": "block", "value": "violence", "severity": "critical" }, + { "rule_type": "category", "action": "block", "value": "adult", "severity": "critical" }, + { "rule_type": "keyword", "action": "flag", "value": "accident", "severity": "medium" } + ], + "brand": { + "domain": "automaker.com" + } +} +``` + +**Key fields:** +- `name` (string, required): Human-readable name +- `rules` (array, optional): Content rules — `rule_type`, `action` (allow/block/flag), `value`, `severity` +- `brand` (object, optional): Brand reference for automatic rule inference + +--- + +### get_content_standards + +Retrieve content standards by ID. + +**Request:** +```json +{ + "standards_id": "cs_abc123" +} +``` + +--- + +### update_content_standards + +Modify existing content standards. + +**Request:** +```json +{ + "standards_id": "cs_abc123", + "rules": [ + { "rule_type": "category", "action": "block", "value": "violence", "severity": "critical" } + ] +} +``` + +--- + +### list_content_standards + +List all content standards accessible to the authenticated principal. + +**Request:** +```json +{ + "name_contains": "automotive" +} +``` + +--- + +### calibrate_content + +Test content samples against content standards to validate configuration. + +**Request:** +```json +{ + "standards_id": "cs_abc123", + "samples": [ + { "url": "https://example.com/article1", "expected_result": "allow" }, + { "url": "https://example.com/article2", "expected_result": "block" }, + { "text": "Car crash injures three people", "expected_result": "block" } + ] +} +``` + +**Key fields:** +- `standards_id` (string, required): Content standards to calibrate against +- `samples` (array, required): Content samples with `url` and/or `text`, and optional `expected_result` + +--- + +### get_media_buy_artifacts + +Get creative artifacts from a media buy for compliance review. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "sales_agent_url": "https://sales.publisher.com" +} +``` + +**Key fields:** +- `media_buy_id` (string, required): Media buy identifier +- `sales_agent_url` (string, required): Sales agent that owns the media buy + +--- + +### validate_content_delivery + +Validate delivered content against content standards. + +**Request:** +```json +{ + "standards_id": "cs_abc123", + "media_buy_id": "mb_abc123", + "sales_agent_url": "https://sales.publisher.com", + "date_range": { + "start": "2025-01-01", + "end": "2025-01-31" + } +} +``` + +**Key fields:** +- `standards_id` (string, required): Content standards to validate against +- `media_buy_id` (string, required): Media buy identifier +- `sales_agent_url` (string, required): Sales agent URL +- `date_range` (object, optional): Filter by delivery date range + +--- + +### sync_plans + +Register or update a campaign plan that defines authorized parameters (budget, channels, policies) for an orchestrator's autonomous action. + +**Request:** +```json +{ + "plan_id": "plan_q1_2026_launch", + "plan_version": 1, + "budget": { "authorized": 500000, "currency": "USD" }, + "channel_allocation": { "olv": 0.55, "display": 0.30, "audio": 0.15 }, + "policies": ["us_coppa", "alcohol_advertising"], + "human_review_required": false +} +``` + +**Key fields:** +- `plan_id` (string, required): Stable plan identifier +- `plan_version` (integer, required): Increment on every modification — checks bind to a version +- `budget.authorized` (number, required): Total spend authority across all governed actions on this plan +- `policies` (array, optional): Registry policy IDs that govern this plan. Inline `custom_policies` may add restrictions but cannot relax registry policies. +- `human_review_required` (boolean, optional): Force human review on all actions. Auto-set true when any resolved policy has `requires_human_review: true`. + +--- + +### check_governance + +Validate an action against the plan. Called twice in the lifecycle: once on intent (pre-discovery), once on execution (pre-buy). Sellers MUST call execution checks independently using credentials synced via `sync_governance`. + +**Request (execution check):** +```json +{ + "plan_id": "plan_q1_2026_launch", + "plan_version": 1, + "purchase_type": "media_buy", + "tool": "create_media_buy", + "payload": { "...": "the create_media_buy request body" }, + "governance_context": "gc_mb_seller_456" +} +``` + +**Key fields:** +- `purchase_type` (enum, required): `media_buy`, `rights_license`, `signal_activation`, or `creative_services` +- `governance_context` (string, optional): Echoed on subsequent checks for the same governed action. The agent issues this on the first check and the buyer attaches it to the action envelope. +- `tool` (string, required): Which AdCP tool is being authorized +- `payload` (object, required): The full request body the orchestrator/seller would otherwise send + +**Response status:** `approved`, `denied`, or `conditions`. On `denied`, read `governance_context.findings[]` to locate the failed rule and correct the payload. + +--- + +### report_plan_outcome + +Report the result of a governed action so plan budget and state advance. + +**Request:** +```json +{ + "plan_id": "plan_q1_2026_launch", + "governance_context": "gc_mb_seller_456", + "outcome": "completed", + "committed_budget": 150000 +} +``` + +**Key fields:** +- `governance_context` (string, required): The token issued on the original check +- `outcome` (enum, required): `completed`, `cancelled`, `failed`, or `delivery` (for ongoing pacing reports) +- `committed_budget` (number, required for `completed`): Net budget committed by this action + +--- + +### get_plan_audit_logs + +Retrieve governance state, budget tracking, and audit trail for one or more plans. + +**Request:** +```json +{ + "plan_ids": ["plan_q1_2026_launch"], + "governance_contexts": ["gc_mb_seller_456"], + "include_entries": true +} +``` + +**Key fields:** +- `plan_ids` / `portfolio_plan_ids` / `governance_contexts` (at least one required): Scope the query +- `include_entries` (boolean, optional): Return the full audit trail. Default `false` returns summary only. + +**Producing a shareable view:** filter `governance_contexts` to the requesting party's actions and strip plan-level aggregates (`budget.*`, `channel_allocation.*`, `summary.drift_metrics`) before forwarding. See [audit trail: internal vs shareable views](/docs/governance/campaign/audit-trail). + +--- + +## Key Concepts + +### Property Lists vs Collection Lists + +- **Property Lists**: Site-level targeting. Operates on publisher domains and properties (websites, apps, CTV apps). Use for "where" the ad appears. +- **Collection Lists**: Program-level targeting. Operates on shows, series, and content programs using distribution identifiers (IMDb, Gracenote, EIDR). Use for "what content" the ad appears alongside (primarily CTV). + +### Content Standards vs Property/Collection Lists + +- **Content Standards**: Rules-based evaluation of content quality, topics, and safety. Evaluates content dynamically. +- **Property/Collection Lists**: Pre-computed sets of approved or excluded inventory. Static targeting applied at campaign setup. + +### Filter Resolution + +Property and collection lists combine static selections with dynamic filters. Use `resolve: true` on get operations to see the final resolved set of properties or collections. + +### Three invariants for audit and disclosure decisions + +These three properties of campaign governance shape what an orchestrator can disclose, can rely on a counterparty having, and cannot work around. Surface them when audit-trail design or counterparty disclosure decisions come up. + +1. **Inline policies are additive-only over registry policies.** A buyer's bespoke `custom_policies` (or inline `policy` entries on a plan) may add restrictions on top of registry-sourced policies. They MUST NOT relax, override, or disable registry policies. Counterparties who see `policies_evaluated: ["us_coppa"]` can trust the registry version of `us_coppa` was applied at its declared `enforcement` level. +2. **`governance_context` is the seller-visible correlation token; full plan/budget data is buyer-side.** The seller sees the opaque token they were issued and the entries scoped to it. Plan-level totals (`budget.authorized`, `channel_allocation`, `drift_metrics`) belong to the buyer's internal view and are never shared by default. +3. **`plan_hash` is the cryptographic attestation surface.** `base64url_no_pad(SHA-256(JCS(plan_payload)))` over the plan revision the check evaluated. Any party with the plan revision can recompute and byte-compare. This is what makes a four-field shareable attestation (`governance_context`, `status`, `plan_hash`, `policies_evaluated`) cryptographically meaningful — counterparties don't have to trust the buyer's summary. + +> A related working-group adoption pattern — `effective_date` enabling informational-before-enforcement of new policies — lives in [Policy Registry](/docs/governance/policy-registry); it shapes registry rollout rather than per-check disclosure decisions. + +--- + +## Error Handling + +Common error codes: + +- `LIST_NOT_FOUND`: Invalid list_id +- `STANDARDS_NOT_FOUND`: Invalid standards_id +- `UNAUTHORIZED`: Not authorized to access this resource +- `VALIDATION_ERROR`: Invalid filter or rule configuration +- `PLAN_NOT_FOUND`: No plan with this ID, or the principal is not authorized for it. Returned indistinguishably from the unauthorized case to prevent plan-ID enumeration. +- `GOVERNANCE_DENIED`: `check_governance` rejected the action. Read `governance_context.findings[]` to identify the failed rule, correct the payload, and retry. diff --git a/skills/adcp-media-buy/SKILL.md b/skills/adcp-media-buy/SKILL.md new file mode 100644 index 0000000..2e3c93c --- /dev/null +++ b/skills/adcp-media-buy/SKILL.md @@ -0,0 +1,446 @@ +--- +name: adcp-media-buy +description: Execute AdCP Media Buy Protocol operations with sales agents - discover advertising products, create and manage campaigns, sync creatives, and track delivery. Use when users want to buy advertising, create media buys, interact with ad sales agents, or test advertising APIs. +--- + +# AdCP Media Buy Protocol + +This skill enables you to execute the AdCP Media Buy Protocol with sales agents. Use the standard MCP tools (`get_products`, `create_media_buy`, `sync_creatives`, etc.) exposed by the connected agent. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Media Buy Protocol provides 11 standardized tasks for managing advertising campaigns: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `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 | +| `get_media_buys` | Retrieve campaign state and status | ~1-5s | +| `sync_creatives` | Upload creative assets | Minutes-Days | +| `sync_catalogs` | Sync product feeds and catalogs | Minutes-Days | +| `list_creatives` | Query creative library | ~1s | +| `get_media_buy_delivery` | Get performance data | ~60s | +| `provide_performance_feedback` | Share outcomes with publishers | ~1-5s | + +## 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 + +--- + +## Task Reference + +### get_products + +Discover advertising products using natural language briefs. + +**Request:** +```json +{ + "buying_mode": "brief", + "brief": "Looking for premium video inventory for a tech brand targeting developers", + "brand": { + "domain": "example.com" + }, + "filters": { + "channels": ["video", "ctv"], + "budget_range": { "min": 5000, "max": 50000 } + } +} +``` + +**Key fields:** +- `buying_mode` (string): Required discriminator - `"brief"` or `"wholesale"` +- `brief` (string): Natural language description of campaign requirements +- `brand` (object): Brand identity - `{ "domain": "acmecorp.com" }` +- `filters` (object, optional): Filter by channels, budget, delivery_type + +**Response contains:** +- `products`: Array of matching products with `product_id`, `name`, `description`, `pricing_options` +- Each product includes `format_ids` (supported creative formats) and `targeting` (available targeting) + +--- + +### list_authorized_properties + +Get the list of publisher properties this agent can sell. + +**Request:** +```json +{} +``` + +No parameters required. + +**Response contains:** +- `publisher_domains`: Array of domain strings the agent is authorized to sell + +--- + +### list_creative_formats + +View supported creative specifications. + +**Request:** +```json +{ + "asset_types": ["video", "image"] +} +``` + +**Key fields:** +- `asset_types` (array, optional): Filter by asset types (image, video, audio, text, html, vast, etc.) +- `name_search` (string, optional): Case-insensitive partial match on name or description + +**Response contains:** +- `formats`: Array of format specifications with dimensions, requirements, and asset schemas + +--- + +### create_media_buy + +Create an advertising campaign from selected products. + +**Request:** +```json +{ + "brand": { + "domain": "acme.com" + }, + "packages": [ + { + "product_id": "premium_video_30s", + "pricing_option_id": "cpm-standard", + "budget": 10000 + } + ], + "start_time": { + "type": "asap" + }, + "end_time": "2024-03-31T23:59:59Z" +} +``` + +**Key fields:** +- `brand` (object, required): Brand identity - `{ "domain": "acmecorp.com" }` +- `packages` (array, required): Products to purchase, each with: + - `product_id`: From `get_products` response + - `pricing_option_id`: From product's `pricing_options` + - `budget`: Amount in dollars + - `bid_price`: Required for auction pricing + - `targeting_overlay`: Additional targeting constraints + - `creative_ids` or `creatives`: Creative assignments +- `start_time` (object, required): `{ "type": "asap" }` or `{ "type": "scheduled", "datetime": "..." }` +- `end_time` (string, required): ISO 8601 datetime + +**Response contains:** +- `media_buy_id`: The created campaign identifier +- `status`: Current lifecycle state — `pending_creatives` (no creatives assigned yet), `pending_start` (waiting for flight date), or `active` (serving immediately) +- `packages`: Created packages with their IDs + +--- + +### update_media_buy + +Modify an existing campaign. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "updates": { + "budget_change": 5000, + "end_time": "2024-04-30T23:59:59Z", + "status": "paused" + } +} +``` + +**Key fields:** +- `media_buy_id` (string, required): The campaign to update +- `updates` (object): Changes to apply - budget_change, end_time, status, targeting, etc. + +--- + +### sync_catalogs + +Sync product catalogs, store locations, job postings, and other structured feeds to a seller account. Supports inline items or external feed URLs. When called without catalogs, returns existing catalogs (discovery mode). + +**Request:** +```json +{ + "account": { + "account_id": "acct_123" + }, + "catalogs": [ + { + "catalog_id": "winter-collection", + "name": "Winter 2025 Collection", + "type": "product", + "items": [ + { "id": "sku-001", "name": "Wool Coat", "price": 299.99, "currency": "USD" } + ] + } + ] +} +``` + +**Key fields:** +- `account` (object, required): Account that owns the catalogs — `{ account_id }` +- `catalogs` (array, optional): Catalog objects to sync. Omit for discovery mode. + - `type` (string, required): `offering`, `product`, `inventory`, `store`, `promotion`, `hotel`, `flight`, `job`, `vehicle`, `real_estate`, `education`, `destination`, `app` + - `items` (array): Inline catalog data (mutually exclusive with `url`) + - `url` (string): External feed URL (mutually exclusive with `items`) + - `feed_format` (string): `google_merchant_center`, `facebook_catalog`, `shopify`, `linkedin_jobs`, `custom` +- `delete_missing` (boolean, optional): Remove catalogs not in this sync (use with caution) +- `dry_run` (boolean, optional): Preview changes without applying + +--- + +### sync_creatives + +Upload and manage creative assets. + +**Request:** +```json +{ + "creatives": [ + { + "creative_id": "hero_video_30s", + "name": "Brand Hero Video", + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "video_standard_30s" + }, + "assets": { + "video": { + "url": "https://cdn.example.com/hero.mp4", + "width": 1920, + "height": 1080, + "duration_ms": 30000 + } + } + } + ], + "assignments": { + "hero_video_30s": ["pkg_001", "pkg_002"] + } +} +``` + +**Key fields:** +- `creatives` (array, required): Creative assets to sync + - `creative_id`: Your unique identifier + - `format_id`: Object with `agent_url` and `id` from format specifications + - `assets`: Asset content (video, image, html, etc.) +- `assignments` (object, optional): Map creative_id to package IDs +- `dry_run` (boolean): Preview changes without applying +- `delete_missing` (boolean): Archive creatives not in this sync + +--- + +### list_creatives + +Query the creative library with filtering. + +**Request:** +```json +{ + "filters": { + "status": ["active"] + }, + "limit": 20 +} +``` + +--- + +### get_media_buys + +Retrieve media buy state: status, valid_actions, creative approvals, pending formats, and optional delivery snapshots or revision history. + +**Request:** +```json +{ + "media_buy_ids": ["mb_abc123"], + "include_snapshot": true, + "include_history": 5 +} +``` + +**Key fields:** +- `media_buy_ids` (array, optional): Specific media buy IDs to retrieve +- `account` (object, optional): Filter to a specific account +- `status_filter` (string or array, optional): Filter by status — `pending_creatives`, `pending_start`, `active`, `paused`, `completed`, `rejected`, `canceled`. Defaults to `["active"]` when no IDs provided. +- `include_snapshot` (boolean, optional): Include near-real-time delivery snapshots per package +- `include_history` (integer, optional): Include the last N revision history entries per media buy + +**Response contains:** +- `media_buys`: Array with `media_buy_id`, `status`, `valid_actions`, `packages`, creative approval state +- Optional `snapshot` per package (impressions, spend, pacing) +- Optional `history` entries (revision, timestamp, actor, action, summary) + +--- + +### provide_performance_feedback + +Share performance outcomes with publishers to enable data-driven optimization. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "measurement_period": { + "start": "2025-01-01T00:00:00Z", + "end": "2025-01-31T23:59:59Z" + }, + "performance_index": 1.2, + "metric_type": "conversion_rate", + "feedback_source": "buyer_attribution" +} +``` + +**Key fields:** +- `media_buy_id` (string, required): Publisher's media buy identifier +- `measurement_period` (object, required): Time period with `start` and `end` (ISO 8601) +- `performance_index` (number, required): Normalized score — 0.0 = no value, 1.0 = expected, >1.0 = above expected +- `package_id` (string, optional): Specific package for package-level feedback +- `creative_id` (string, optional): Specific creative for creative-level feedback +- `metric_type` (string, optional): `overall_performance`, `conversion_rate`, `brand_lift`, `click_through_rate`, `completion_rate`, `viewability`, `brand_safety`, `cost_efficiency` +- `feedback_source` (string, optional): `buyer_attribution`, `third_party_measurement`, `platform_analytics`, `verification_partner` + +--- + +### get_media_buy_delivery + +Retrieve performance metrics for a campaign. + +**Request:** +```json +{ + "media_buy_id": "mb_abc123", + "granularity": "daily", + "date_range": { + "start": "2024-01-01", + "end": "2024-01-31" + } +} +``` + +**Response contains:** +- `delivery`: Aggregated metrics (impressions, spend, clicks, etc.) +- `by_package`: Breakdown by package +- `timeseries`: Data points over time if granularity specified + +--- + +## Key Concepts + +### Brand identity + +Brand context is provided by domain reference: + +```json +{ + "brand": { + "domain": "acmecorp.com" + } +} +``` + +The agent resolves the domain to retrieve the brand's identity (name, colors, guidelines, etc.) from its `brand.json` file. + +### Format IDs + +Creative format identifiers are structured objects: +```json +{ + "format_id": { + "agent_url": "https://creative.adcontextprotocol.org", + "id": "display_300x250" + } +} +``` + +The `agent_url` specifies which creative agent defines the format. Use `https://creative.adcontextprotocol.org` for standard IAB formats. + +### Pricing Options + +Products include `pricing_options` array. Each option has: +- `pricing_option_id`: Use this in `create_media_buy` +- `pricing_model`: "cpm", "cpm-auction", "flat-fee", etc. +- `price`: Base price (for fixed pricing) +- `floor`: Minimum bid (for auction) + +For auction pricing, include `bid_price` in your package. + +### Asynchronous Operations + +Operations like `create_media_buy` and `sync_creatives` may require human approval. The response includes: +- `status: "pending"` - Operation awaiting approval +- `task_id` - For tracking async progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error patterns: + +- **400 Bad Request**: Invalid parameters - check required fields +- **401 Unauthorized**: Invalid or missing authentication token +- **404 Not Found**: Invalid product_id, media_buy_id, or creative_id +- **422 Validation Error**: Schema validation failure - check field types + +Error responses include: +```json +{ + "errors": [ + { + "code": "VALIDATION_ERROR", + "message": "budget must be greater than 0", + "field": "packages[0].budget" + } + ] +} +``` + +--- + +## Testing Mode + +Use **sandbox mode** for testing without real transactions. Sandbox is account-level — once a request references a sandbox account, the entire request is treated as sandbox with no real platform calls or spend. + +Check whether the agent supports sandbox via `get_adcp_capabilities`: +```json +{ + "account": { + "sandbox": true + } +} +``` + +To enter sandbox mode, set `sandbox: true` on the account reference: +```json +{ + "account": { + "brand": { "domain": "acme-corp.com" }, + "operator": "acme-corp.com", + "sandbox": true + } +} +``` + +Some sync tasks (`sync_creatives`, `sync_catalogs`) also support a `dry_run` parameter that previews changes without applying them. This is orthogonal to sandbox — you can use `dry_run` in both sandbox and production accounts. + +See [Sandbox mode](https://docs.adcontextprotocol.org/docs/media-buy/advanced-topics/sandbox) for full details. diff --git a/skills/adcp-si/SKILL.md b/skills/adcp-si/SKILL.md new file mode 100644 index 0000000..1d1b297 --- /dev/null +++ b/skills/adcp-si/SKILL.md @@ -0,0 +1,206 @@ +--- +name: adcp-si +description: Execute AdCP Sponsored Intelligence (SI) Protocol operations with brand agents - start conversational sessions, send messages, preview offerings, and manage session lifecycle. Use when users want to have conversations with brand agents, explore product offerings, or manage sponsored interactions. +--- + +# AdCP Sponsored Intelligence (SI) Protocol + +This skill enables you to execute the AdCP SI Protocol with brand agents. SI enables conversational commerce sessions where users engage directly with brand agents for shopping, inquiries, and transactions. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The SI Protocol provides 4 standardized tasks for managing conversational sessions: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `si_initiate_session` | Start a brand conversation | ~2-5s | +| `si_send_message` | Send a message in an active session | ~1-5s | +| `si_get_offering` | Preview offerings before starting | ~1-3s | +| `si_terminate_session` | End a session | ~1s | + +## Typical Workflow + +1. **Preview** (optional): `si_get_offering` to see what the brand offers before consent +2. **Start session**: `si_initiate_session` with the user's `intent` and consent +3. **Converse**: `si_send_message` to relay user messages and action responses +4. **End**: `si_terminate_session` when done + +--- + +## Task Reference + +### si_initiate_session + +Start a conversational session with a brand agent. + +**Request:** +```json +{ + "intent": "I'm interested in your winter jacket collection", + "identity": { + "consent_granted": true, + "consent_timestamp": "2025-01-15T10:30:00Z", + "consent_scope": ["email", "name"], + "user": { + "email": "user@example.com", + "name": "Jane Smith", + "locale": "en-US" + } + }, + "placement": "chatgpt_search" +} +``` + +**Key fields:** +- `intent` (string, required): Natural language description of user intent — the conversation handoff from host to brand agent +- `identity` (object, required): User identity with consent status + - `consent_granted` (boolean, required): Whether user consented to share identity + - `consent_timestamp` (string, optional): ISO 8601 timestamp of consent + - `consent_scope` (array, optional): Fields user agreed to share + - `user` (object, optional): PII (only if consent_granted is true) — `email`, `name`, `locale` + - `anonymous_session_id` (string, optional): Session ID if no consent +- `media_buy_id` (string, optional): AdCP media buy ID if triggered by advertising +- `placement` (string, optional): Where the session was triggered +- `offering_id` (string, optional): Brand-specific offering reference +- `offering_token` (string, optional): Token from `si_get_offering` for session continuity +- `supported_capabilities` (object, optional): Host platform capabilities (modalities, components, commerce) +- `context` (object, optional): Opaque correlation data (e.g., `{"trace_id": "abc-123"}`) echoed unchanged in the response — never parsed by the brand agent + +**Response contains:** +- `session_id`: Use in subsequent `si_send_message` and `si_terminate_session` calls +- `greeting`: Brand agent's initial message +- `suggested_actions`: Optional UI elements (buttons, quick replies) + +--- + +### si_send_message + +Send a message within an active SI session. + +**Text message:** +```json +{ + "session_id": "sess_abc123", + "message": "Do you have this in size medium?" +} +``` + +**Action response (button click, form submit):** +```json +{ + "session_id": "sess_abc123", + "action_response": { + "action": "add_to_cart", + "element_id": "btn_add_cart_sku789", + "payload": { + "size": "M", + "color": "navy" + } + } +} +``` + +**Key fields:** +- `session_id` (string, required): Session ID from `si_initiate_session` +- `message` (string, conditional): User's text message. Required unless `action_response` is provided. +- `action_response` (object, conditional): Response to a UI action — `action`, `element_id`, `payload`. Required unless `message` is provided. + +**Response contains:** +- `message`: Brand agent's response text +- `suggested_actions`: Optional UI elements for next interaction +- `components`: Optional rich UI components (product cards, carousels, forms) + +--- + +### si_get_offering + +Get offering details and availability before initiating a session. Allows showing rich previews before asking for user consent. + +**Request:** +```json +{ + "offering_id": "winter-collection-2025", + "intent": "Looking for warm jackets under $200", + "include_products": true, + "product_limit": 5 +} +``` + +**Key fields:** +- `offering_id` (string, required): Offering identifier from the catalog +- `intent` (string, optional): Natural language description of user intent for personalized results (no PII) +- `include_products` (boolean, optional): Include matching products +- `product_limit` (number, optional): Max products to return (default 5, max 50) +- `context` (object, optional): Opaque correlation data echoed unchanged in the response — never parsed by the brand agent + +**Response contains:** +- `offering`: Offering details (name, description, availability) +- `products`: Matching products if `include_products` is true +- `offering_token`: Pass to `si_initiate_session` for session continuity + +--- + +### si_terminate_session + +End an SI session. + +**Request:** +```json +{ + "session_id": "sess_abc123", + "reason": "user_exit" +} +``` + +**Key fields:** +- `session_id` (string, required): Session ID to terminate +- `reason` (string, required): Why the session is ending — `handoff_transaction`, `handoff_complete`, `user_exit`, `session_timeout`, `host_terminated` +- `termination_context` (object, optional): Conversation summary, transaction intent, and cause for the termination +- `context` (object, optional): Opaque correlation data echoed unchanged in the response — never parsed by the brand agent + +**Reason values:** +- `handoff_transaction`: User is being redirected to complete a transaction +- `handoff_complete`: Transaction completed within the session +- `user_exit`: User chose to leave +- `session_timeout`: Session timed out +- `host_terminated`: Host platform ended the session + +--- + +## Key Concepts + +### Consent Model + +SI sessions require explicit user consent before sharing PII: +- `consent_granted: false` + `anonymous_session_id`: Anonymous session +- `consent_granted: true` + `user` object: Personalized session with identity + +### Session Lifecycle + +``` +si_get_offering (optional) → si_initiate_session → si_send_message (repeat) → si_terminate_session +``` + +Sessions are stateful. The brand agent maintains context across messages within a session. + +### Placements + +Where the SI session was triggered: +- `chatgpt_search`: Within ChatGPT search results +- `publisher_article`: On a publisher's article page +- `social_feed`: In a social media feed +- `ctv_overlay`: On a CTV streaming overlay + +--- + +## Error Handling + +Common error codes: + +- `SESSION_NOT_FOUND`: Invalid or expired session_id +- `SESSION_EXPIRED`: Session timed out +- `CONSENT_REQUIRED`: Attempting to share PII without consent +- `OFFERING_NOT_FOUND`: Invalid offering_id +- `RATE_LIMITED`: Too many messages in quick succession diff --git a/skills/adcp-signals/SKILL.md b/skills/adcp-signals/SKILL.md new file mode 100644 index 0000000..0b8deca --- /dev/null +++ b/skills/adcp-signals/SKILL.md @@ -0,0 +1,204 @@ +--- +name: adcp-signals +description: Execute AdCP Signals Protocol operations with signal agents - discover audience signals using natural language and activate them on DSPs or sales agents. Use when users want to find targeting data, activate audience segments, or work with signal providers. +--- + +# AdCP Signals Protocol + +This skill enables you to execute the AdCP Signals Protocol with signal agents. Use the standard MCP tools (`get_signals`, `activate_signal`) exposed by the connected agent. + +> **Buyer-side basics** — idempotency replay, `oneOf` variants, async `status:'submitted'` polling, error recovery from `adcp_error.issues[]` — live in `skills/call-adcp-agent/SKILL.md`. This skill covers per-task semantics only. + +## Overview + +The Signals Protocol provides 2 standardized tasks for discovering and activating targeting data: + +| Task | Purpose | Response Time | +|------|---------|---------------| +| `get_signals` | Discover signals using natural language | ~60s | +| `activate_signal` | Activate a signal on a platform/agent | Minutes-Hours | + +## Typical Workflow + +1. **Discover signals**: `get_signals` with a natural language description of targeting needs +2. **Review options**: Evaluate signals by coverage, pricing, and deployment status +3. **Activate if needed**: `activate_signal` for signals not yet live on your platform +4. **Use in campaigns**: Reference the activation key in your media buy targeting + +--- + +## Task Reference + +### get_signals + +Discover signals based on natural language description, with deployment status across platforms. + +**Request:** +```json +{ + "signal_spec": "High-income households interested in luxury goods", + "destinations": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" + } + ], + "countries": ["US"], + "filters": { + "max_cpm": 5.0, + "catalog_types": ["marketplace"] + }, + "max_results": 5 +} +``` + +**Key fields:** +- `signal_spec` (string, conditional): Natural language description of desired signals. Required unless `signal_ids` is provided. +- `destinations` (array, optional): Filter signals to those activatable on specific agents/platforms. When omitted, returns all signals available on the current agent. Each item: `type`, `platform`/`agent_url`, optional `account`. +- `countries` (array, optional): ISO 3166-1 alpha-2 country codes where signals will be used +- `filters` (object, optional): Filter by `catalog_types`, `data_providers`, `max_cpm`, `min_coverage_percentage` +- `max_results` (number, optional): Limit number of results + +**Deployment types:** +```json +// DSP platform +{ "type": "platform", "platform": "the-trade-desk", "account": "agency-123" } + +// Sales agent +{ "type": "agent", "agent_url": "https://salesagent.example.com" } +``` + +**Response contains:** +- `signals`: Array of matching signals with: + - `signal_agent_segment_id`: Use this in `activate_signal` + - `name`, `description`: Human-readable signal info + - `data_provider`: Source of the signal data + - `coverage_percentage`: Reach relative to agent's population + - `deployments`: Status per platform with `is_live`, `activation_key`, `estimated_activation_duration_minutes` + - `pricing`: CPM and currency + +--- + +### activate_signal + +Activate a signal for use on a specific platform or agent. + +**Request:** +```json +{ + "signal_agent_segment_id": "luxury_auto_intenders", + "deployments": [ + { + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123-ttd" + } + ] +} +``` + +**Key fields:** +- `signal_agent_segment_id` (string, required): From `get_signals` response +- `deployments` (array, required): Target deployment(s) with `type`, `platform`/`agent_url`, and optional `account` + +**Response contains:** +- `deployments`: Array with activation results per target + - `activation_key`: The key to use for targeting (segment ID or key-value pair) + - `deployed_at`: ISO timestamp when activation completed + - `estimated_activation_duration_minutes`: Time remaining if async +- `errors`: Any warnings or errors encountered + +--- + +## Key Concepts + +### Deployment Targets + +Signals can be activated on two types of targets: + +**DSP Platforms:** +```json +{ + "type": "platform", + "platform": "the-trade-desk", + "account": "agency-123" +} +``` + +**Sales Agents:** +```json +{ + "type": "agent", + "agent_url": "https://wonderstruck.salesagents.com" +} +``` + +### Activation Keys + +When signals are live, the response includes an activation key for targeting: + +**Segment ID format (typical for DSPs):** +```json +{ + "type": "segment_id", + "segment_id": "ttd_segment_12345" +} +``` + +**Key-Value format (typical for sales agents):** +```json +{ + "type": "key_value", + "key": "audience_segment", + "value": "luxury_auto_intenders" +} +``` + +### Signal Types + +- **marketplace**: Licensed from data providers (CPM pricing) +- **custom**: Built for specific principal accounts +- **owned**: Private signals from your own data (no cost) + +### Coverage Percentage + +Indicates signal reach relative to the agent's population: +- 99%: Very broad signal (matches most identifiers) +- 50%: Medium signal +- 1%: Very niche signal + +### Asynchronous Operations + +Signal activation may take time. Check the response: +- `is_live: true` + `activation_key`: Ready to use immediately +- `is_live: false` + `estimated_activation_duration_minutes`: Activation in progress + +Poll or use webhooks to check completion status. + +--- + +## Error Handling + +Common error codes: + +- `SIGNAL_AGENT_SEGMENT_NOT_FOUND`: Invalid signal_agent_segment_id +- `ACTIVATION_FAILED`: Could not activate signal +- `ALREADY_ACTIVATED`: Signal already active on target +- `DEPLOYMENT_UNAUTHORIZED`: Not authorized for platform/account +- `AGENT_NOT_FOUND`: Private agent not visible to this principal +- `AGENT_ACCESS_DENIED`: Not authorized for this signal agent + +Error responses include: +```json +{ + "errors": [ + { + "code": "DEPLOYMENT_UNAUTHORIZED", + "message": "Account not authorized for this data provider", + "field": "deployment.account", + "suggestion": "Contact your account manager to enable access" + } + ] +} +``` diff --git a/skills/call-adcp-agent/SKILL.md b/skills/call-adcp-agent/SKILL.md new file mode 100644 index 0000000..d3497a6 --- /dev/null +++ b/skills/call-adcp-agent/SKILL.md @@ -0,0 +1,255 @@ +--- +name: call-adcp-agent +description: Wire-level invariants for any AdCP buyer call — idempotency_key replay semantics, account `oneOf` variants, async `status:'submitted'`+`task_id` polling, error recovery from `adcp_error.issues[]`. Load before any per-protocol task skill (adcp-media-buy, adcp-creative, adcp-signals, adcp-governance, adcp-si, adcp-brand) when calling an AdCP agent as a buyer. +adcp_version: "3.x" +type: cross-cutting +--- + +# Call an AdCP agent + +## Overview + +AdCP (Ad Context Protocol) agents expose a fixed tool surface (`get_products`, `create_media_buy`, `get_signals`, …) over MCP or A2A. Tool names come from `get_adcp_capabilities`; exact request/response shapes come from `get_schema(tool_name)` when the agent exposes it, otherwise from the bundled JSON Schemas your SDK ships (the layout differs by SDK — see "Discovery chain" below). This skill teaches the invariants that don't live cleanly in any schema: cross-tool patterns, async flow, error recovery. + +## When to Use + +- User wants to call a publisher / SSP / retail media network over AdCP +- Tool names like `get_products`, `create_media_buy`, `sync_creatives`, `get_signals` appear in the available-tools list +- Agent card advertises `protocolVersion: '0.3.0'` with `skills` listing AdCP tool names +- **Not this skill:** building an AdCP seller agent (see `@adcp/client/skills/build-seller-agent/` and analogous SDK skills) + +## Discovery chain + +Walk these in order on first contact: + +1. **Agent card** (A2A) or **`tools/list`** (MCP): returns tool NAMES. AdCP MCP servers no longer publish per-tool parameter schemas in `tools/list` — everything shows `{type: 'object', properties: {}}`. Don't try to infer shape from here. +2. **`get_adcp_capabilities`**: returns supported protocols (`media_buy`, `signals`, `creative`, …), AdCP major versions, feature flags. Tells you WHICH tools this agent supports, not how to call them. +3. **`get_schema(tool_name)`** *(when the agent exposes it — pending standardization in [#3057](https://github.com/adcontextprotocol/adcp/issues/3057), not yet universal)*: returns the JSON Schema for a tool's request/response. Preferred over reading bundled schemas when available. +4. **Bundled schemas** (offline, authoritative): every SDK ships the AdCP JSON Schemas locally. Path differs by SDK — spec repo source uses `dist/schemas//bundled/`, `@adcp/client` puts them at `schemas/cache//bundled/` after `npm run sync-schemas`, Python and Go SDKs use their own conventions. **Don't hardcode a path** — let the SDK's loader find them, or ask the developer. Each schema is `/-{request,response}.json` once you locate the bundle. The canonical source for every SDK is `https://adcontextprotocol.org/protocol/.tgz`. + +## Non-obvious rules every buyer must follow + +### `idempotency_key` is required on every mutating tool + +UUID format. The key is your retry-safety guarantee — and the most common way naive callers create duplicate media buys is by misunderstanding it: + +- **Same key on retry → replay.** The server returns the SAME response — same `task_id`, same `media_buy_id`, same shape, byte-for-byte. Use this for transport-level retries (timeout, 5xx, dropped connection). +- **Fresh key on retry → NEW operation.** Generating a new UUID because the previous attempt failed is how you double-book. Reuse the key until you've seen a terminal response (success, error, or non-retryable error). +- **Same key, different body → server-defined.** Most agents return the original cached response and ignore the body change. Don't rely on it — pick a fresh key only when you genuinely want a new operation. +- For async flows, the replayed response carries the **same `task_id`**, so polling continues against the same task instead of forking a duplicate. + +Required on: `create_media_buy`, `update_media_buy`, `sync_creatives`, `sync_audiences`, `sync_accounts`, `sync_catalogs`, `sync_event_sources`, `sync_plans`, `sync_governance`, `activate_signal`, `acquire_rights`, `log_event`, `report_usage`, `provide_performance_feedback`, `report_plan_outcome`, `create_property_list`, `update_property_list`, `delete_property_list`, `create_collection_list`, `update_collection_list`, `delete_collection_list`, `create_content_standards`, `update_content_standards`, `calibrate_content`, `si_initiate_session`, `si_send_message`. + +Missing the key → `adcp_error.code: 'VALIDATION_ERROR'` with `/idempotency_key` in `issues`. + +### `account` is a `oneOf` — pick ONE variant, send ONLY its fields + +Probably the single most common stumble for naive LLMs. `account` is a discriminated union. Per AdCP 3.0, two variants on `create_media_buy` / `update_media_buy`: + +```json +// variant 0: by seller-assigned id (from sync_accounts or list_accounts) +"account": { "account_id": "seller_assigned_id" } + +// variant 1: by natural key (brand + operator, optional sandbox) +"account": { "brand": { "domain": "acme.com" }, "operator": "sales.example" } +``` + +**Do NOT merge required fields across variants** — `additionalProperties: false` on each variant means `{account_id, brand}` fails BOTH. Pick one variant and send only its fields. Always check the specific tool's schema because other tools (e.g. `sync_creatives`) may accept a superset. + +### `brand` takes `{domain}` — not `{brand_id}` + +```json +"brand": { "domain": "acme.example" } +``` + +### Async responses: `status: 'submitted'` means "queued, poll later" + +A mutating tool can return one of three shapes: + +```json +// Success (sync): the work is done +{ "media_buy_id": "mb_123", "packages": [...], "confirmed_at": "..." } + +// Submitted (async): the work is queued +{ "status": "submitted", "task_id": "tk_abc", "message": "Awaiting IO signature" } + +// Error: don't retry without fixing +{ "errors": [{ "code": "PRODUCT_NOT_FOUND", "message": "..." }] } +``` + +When you see `status: 'submitted'`, the work is NOT complete. Poll via `tasks/get` (A2A) or the MCP async task extension, using the `task_id`. Over A2A the AdCP `task_id` also rides on `artifact.metadata.adcp_task_id` — both work. + +### `packages[*]` on media buys + +```json +"packages": [ + { "buyer_ref": "pkg_1", "product_id": "p_from_catalog", "budget": 10000, "pricing_option_id": "po_xyz" } +] +``` + +`budget` is a **number** (not `{amount, currency}` — currency is implied by the pricing option). Required per package: `product_id`, `budget`, `pricing_option_id`. `buyer_ref` is optional but strongly recommended as a buyer-side correlation id across retries and reporting. + +## Error envelope — read `issues[]` to recover + +Every validation failure produces: + +```json +{ + "adcp_error": { + "code": "VALIDATION_ERROR", + "recovery": "correctable", + "field": "/first/offending/pointer", + "issues": [ + { + "pointer": "/account", + "keyword": "oneOf", + "message": "must match exactly one schema in oneOf", + "variants": [ + { "index": 0, "required": ["account_id"], "properties": ["account_id"] }, + { "index": 1, "required": ["brand", "operator"], "properties": ["brand", "operator", "sandbox"] } + ] + }, + { "pointer": "/brand/domain", "keyword": "required", "message": "must have required property 'domain'" } + ] + } +} +``` + +- `issues[].pointer` — RFC 6901 JSON Pointer to the field. +- `issues[].keyword` — AJV keyword (`required`, `type`, `oneOf`, `anyOf`, `additionalProperties`, `format`, `enum`). +- `issues[].variants` — when the keyword is `oneOf` or `anyOf`, each entry lists one variant's `required` + declared `properties`. **Pick ONE variant**, send only its `required` fields. This is the fastest recovery path when you didn't know the field was a union. + +Patch the pointers, don't re-guess what the skill or the `variants` already told you, resend. Three attempts should cover every field. + +## Minimal working examples + +### get_products + +```json +{ + "buying_mode": "brief", + "brief": "premium CTV sports inventory for live NBA finals in major US markets" +} +``` + +Returns `{ products: [{ product_id, name, description, delivery_type, pricing_options, ... }] }`. + +### create_media_buy + +```json +{ + "idempotency_key": "", + "account": { "account_id": "seller_assigned_id" }, + "brand": { "domain": "acme.example" }, + "start_time": "2026-05-01T00:00:00Z", + "end_time": "2026-05-31T23:59:59Z", + "packages": [ + { + "buyer_ref": "pkg_1", + "product_id": "", + "budget": 10000, + "pricing_option_id": "" + } + ] +} +``` + +If you don't have a `seller_assigned_id`, use the natural-key variant instead: +`"account": { "brand": { "domain": "acme.example" }, "operator": "sales.example" }`. + +Returns **either** `{ media_buy_id, packages: [...], confirmed_at }` (sync) **or** `{ status: 'submitted', task_id, message }` (async — guaranteed / IO-signed flows). + +### sync_creatives + +```json +{ + "idempotency_key": "", + "account": { "account_id": "seller_assigned_id" }, + "creatives": [ + { + "creative_id": "cr_1", + "name": "My Creative", + "format_id": { "agent_url": "https://creatives.adcontextprotocol.org", "id": "video_1920x1080" }, + "assets": {} + } + ] +} +``` + +Per-creative required: `creative_id`, `name`, `format_id: { agent_url, id }`, `assets` (shape depends on `format_id`; start with `{}` then fill required asset keys per format spec). Returns `{ creatives: [{ creative_id, action, status }] }` — items may fail individually without failing the batch. + +### get_signals + +```json +{ + "signal_spec": "female professionals 25-54 in major US metros" +} +``` + +Returns `{ signals: [{ signal_agent_segment_id, match_rate, pricing, ... }] }`. Note: the identifier field is `signal_agent_segment_id` (not `signal_id`) — used as input to `activate_signal` below. + +### activate_signal + +```json +{ + "idempotency_key": "", + "signal_agent_segment_id": "sig_premium_ctv_sports", + "destinations": [ + { "type": "platform", "platform": "the-trade-desk" } + ] +} +``` + +`destinations[]` is a `oneOf`: either `{type: 'platform', platform, account?}` OR `{type, agent_url, account?}`. Pick one shape per destination. + +## Transport notes + +- **MCP**: `tools/call` with `{ name: 'tool_name', arguments: {...} }`. Returns `{ content, structuredContent, isError? }`. Read `structuredContent` for the typed response. +- **A2A**: `message/send` with a `DataPart` of shape `{ skill: 'tool_name', input: {...} }` (the legacy key `parameters` is also accepted). Returns an A2A `Task`; the typed response is at `task.artifacts[0].parts[0].data`. + +Both transports share: idempotency, error shape, schema enforcement, and handler semantics. If a call works on one, the equivalent call works on the other. + +## Gotchas I keep seeing + +1. **Merging `oneOf` variants**: see the account section above. If you see three `additionalProperties` errors under one pointer, you merged. Drop to one variant. +2. **`budget` as an object**: it's a number. Currency comes from the `pricing_option`. +3. **`brand.brand_id` instead of `brand.domain`**: spec uses `domain`. +4. **Forgetting `idempotency_key`**: required on every mutating tool; see the list above. +5. **Treating A2A `Task.state: 'completed'` as AdCP completion**: A2A task state = transport call lifecycle. AdCP-level completion is in the artifact's payload (`structuredContent.status` or `data.status`). A `completed` A2A task can still carry a `submitted` AdCP response. +6. **`format_id` as a string**: `format_id` is always an object `{ agent_url, id }` (and sometimes `{ width, height, duration_ms }` for dimensions). Sending `"format_id": "video_1920x1080"` fails with an `additionalProperties` / `type` error — pass the object. + +## Symptom → fix + +Quick lookup before reading the full envelope. Match what you see in `adcp_error.issues[*]`, apply the fix: + +| Symptom | What it means | Fix | +|---|---|---| +| `keyword: 'oneOf'` with `variants[]` | Discriminated union — you sent fields from multiple variants, or none | Pick ONE variant from `variants[]`. Send only its `required` fields. | +| 2-3 `additionalProperties` errors at the same pointer | You merged `oneOf` variants (`` `{account_id, brand, operator, …}` ``) | Drop to one variant. Don't keep "extra" fields "for completeness". | +| `keyword: 'required'`, `pointer: '/idempotency_key'` | Mutating tool, no UUID | Generate fresh UUID per logical operation. Reuse it on retries. | +| `keyword: 'type'` or `additionalProperties` at `/budget` | Sent `{amount, currency}` | `budget` is a number. Currency is implied by `pricing_option_id`. | +| `additionalProperties` at `/format_id` (string passed) | Sent `"format_id": "video_..."` | `format_id` is `{agent_url, id}` — always an object. | +| `keyword: 'enum'` at `/destinations/*/type` | Made-up destination type | Use `'platform'` (with `platform`) or `'agent'` (with `agent_url`). | +| Response carries `status: 'submitted'` and `task_id` | Async — work is queued, NOT done | Poll via `tasks/get` (A2A) or the MCP async task extension using `task_id`. | +| `recovery: 'transient'` (rate limit, 5xx, timeout) | Server-side, retry-safe | Retry with the **same** `idempotency_key`. | +| `recovery: 'correctable'` | Buyer-side fix | Read `issues[]`, patch the pointers, resend. Most cases close in one attempt. | +| `recovery: 'terminal'` (account suspended, payment required, …) | Requires human action | Don't retry. Surface to the user. | +| HTTP 401 with `WWW-Authenticate` header | Missing or expired credential | Add `Authorization` per the agent's auth spec; re-auth if applicable. | + +If your symptom isn't here, fall through to the next section. + +## If you get stuck + +Priority order: + +1. Re-read the failure's `issues[]`. The pointer list plus this skill covers 80% of cases. +2. Call `get_schema(tool_name)` if the agent exposes it (see [#3057](https://github.com/adcontextprotocol/adcp/issues/3057) for the pending standard). +3. Read the bundled JSON Schema for `/-request.json` — see Discovery chain step 4 for path resolution. If you can't locate the SDK's schema cache, ask the developer or fall back to `get_schema()`. +4. Consult the per-protocol skill (`adcp-media-buy`, `adcp-creative`, …) for specialism-specific patterns. + +## Related + +- [Calling an agent (docs)](https://adcontextprotocol.org/docs/protocol/calling-an-agent) — human-readable narrative form of this skill +- `skills/adcp-media-buy/`, `skills/adcp-creative/`, `skills/adcp-signals/`, `skills/adcp-governance/`, `skills/adcp-si/`, `skills/adcp-brand/` — per-protocol task skills (layered on top of this one) +- `@adcp/client/skills/build-seller-agent/SKILL.md` — building agents on the other side of the call +- Bundled JSON Schemas — canonical for every tool, version-pinned. Path differs by SDK (see Discovery chain step 4). Pulled from the protocol tarball at `/protocol/.tgz`.