Execute AdCP Media Buy Protocol operations with sales agents - discover advertising products, create and manage campaigns, sync creatives, and track delivery...
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,
oneOfvariants, asyncstatus:'submitted'polling, error recovery fromadcp_error.issues[]โ live inskills/call-adcp-agent/SKILL.md. This skill covers per-task semantics only.
3.2 preview: Targeting-aware discovery fields are available only when the seller serves AdCP 3.2+ and the installed SDK exposes the 3.2 schema. Check
get_adcp_capabilities.adcp.supported_versions, pin the selected release inadcp_version, and validate the echoed served release before sending them. A missing release-precision declaration, a 3.1-or-earlier result, or a major-only declaration is not evidence of support: omit the 3.2 fields and use legacy targeting filters or explicit brief prose. Do not probe by sending unknown fields because a legacy open schema may accept and ignore them. The public training agent will implement this flow after the 3.2 beta SDK release; until then use its legacy brief exercise and do not treat ignored unknown fields as acceptance.
The Media Buy Protocol provides these common standardized tasks:
| Task | Purpose | Response Time |
|---|---|---|
list_products |
Read matching offers without seller curation | ~1-5s |
request_proposals |
Request seller-authored draft plans | ~60s or async |
refine_proposals |
Revise drafts or finalize unchanged terms into inventory holds | ~60s or async |
decline_proposals |
Record terminal buyer disposition | ~1-5s |
accept_proposal |
Accept a finalized proposal into a MediaBuy | Minutes-Days |
get_products |
Use the 3.x compatibility facade for discovery and proposals | ~60s |
get_adcp_capabilities |
See agent capabilities, supported protocols, and publisher properties | ~1s |
create_media_buy |
Create direct buys or use the 3.x proposal adapter | 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 |
list_products, or request_proposals with a briefoverlay_support, and any targeting_resolutionaccept_proposal for the held snapshot or decline_proposals when the buyer stops pursuing itsync_creatives to add creative assetsget_media_buy_delivery to track performanceProducts carry format_options[]: a list of ProductFormatDeclaration entries describing the creative shapes the product accepts. Each declaration carries:
format_kind โ one of the 15 canonicals: image, html5, display_tag, image_carousel, video_hosted, video_vast, audio_hosted, audio_vast, audio_daast, sponsored_placement, native_in_feed, responsive_creative, agent_placement, seller_rendered_stateful_display, or coordinated_placements; use custom only with format_shape and format_schemaparams โ per-canonical parameters narrowing the format (dimensions, durations, codecs, char limits, CTA enums)format_option_id โ disambiguates product options and identifies publisher-catalog declarations when paired with publisher_domainv1_format_ref: [{agent_url, id}] โ array linking this v2 declaration to one or more v1 named formats (for dual emission during the v1โv2 migration). Multi-size declarations should carry one ref per sizeseller_preference: "preferred" | "accepted" | "discouraged" โ soft routing hint when a multi-format product has several options at the same priceMulti-format products. A flexible publisher slot is one product with N format_options entries โ e.g., Pinnacle Media's homepage accepts image OR html5 OR display_tag at multiple sizes via three format_options, one per type. Buyer picks the creative type they ship.
Size flexibility. Display canonicals (image / html5 / display_tag) declare size in one of three modes: fixed (width+height), multi-size (sizes: [{w,h}] โ mirrors OpenRTB banner.format[]), or responsive (min_width/max_width/min_height/max_height). Modes are mutually exclusive.
Discovering publisher catalogs. Call GET https://agenticadvertising.org/api/registry/publisher?domain=<publisher_domain> for publisher-origin โ AgenticAdvertising.org community-catalog โ fail-closed resolution and provenance. Add &include=placements for provenance-labeled placement summaries with resolved canonical format options. The lookup's top-level formats[] remains a lossy display summary; fetch the returned raw registry or hosting URL when you need custom schema fields or other omitted declaration fields. Do not infer publisher authority from a seller's product catalog. Seller-specific deliverability comes from that seller's Product.format_options[].
Conversion tracking lives elsewhere. Pixel-firing, conversion events, and attribution belong on sync_event_sources / event_log (campaign-scoped), NOT on creative format declarations. Sending pixel_id in platform_extensions on a format is a category error.
Error codes specific to canonical formats. FORMAT_PROJECTION_FAILED, FORMAT_DECLARATION_DIVERGENT, FORMAT_DECLARATION_V1_AMBIGUOUS, FORMAT_CAPABILITY_UNRESOLVED, FORMAT_DECLARATION_V1_LOSSY_MULTI_SIZE โ all non-fatal advisories surfaced via the response errors[] array. See static/schemas/source/enums/error-code.json for full recovery semantics.
See docs/creative/canonical-formats.mdx for the full vocabulary, narrowing rules, and worked examples.
Discover buyable product configurations. Choose each request surface by what it means:
brief: goals, context, semantic audience intent, preferences, and
requirements without a structured representation.filters: hard offer filters such as metadata, dates, budget, availability,
commercial fit, and reporting support. They decide which products may be
returned and apply in brief, wholesale, and refine.targeting_overlay: exact delivery constraints known now. Use this for
countries, ages, placements, properties, collections, and other typed
targeting so availability, price, and forecast already reflect them.required_overlay_support: targeting dimensions whose values will be chosen
independently on packages later. This requests capability, not one product
per value.Prefer a structured field whenever one exists. It uses fewer tokens, is applied
by code, and avoids lossy inference. Explicit hard targeting written only in a
brief is still binding; when a seller extracts a structured predicate from
prose that materially affects eligibility, pricing, or forecasting, require one response-level confirmation in
GetProductsResponse.targeting_resolution.brief_targeting.
Request:
{
"buying_mode": "brief",
"brief": "Premium video for a developer-tool launch; prioritize engineering and open-source contexts",
"brand": {
"domain": "example.com"
},
"filters": {
"channels": ["olv", "ctv"],
"delivery_type": "guaranteed",
"pricing_currencies": ["USD"]
},
"targeting_overlay": {
"geo_countries": ["US"],
"demographics": {
"age": { "min": 18, "max": 44, "include_unknown": false }
}
},
"required_overlay_support": {
"geo_metros": { "systems": ["nielsen_dma"] }
}
}
Key fields:
buying_mode (string): "brief", "wholesale", or "refine"brief (string): Natural-language curation input; hard statements remain requirementsbrand (object): Brand identity - { "domain": "acmecorp.com" }filters (object, optional): Hard offer filters that decide which products may be returnedtargeting_overlay (object, optional): Concrete targeting applied during discovery and carried into purchaserequired_overlay_support (object, optional): Dimensions the product must allow packages to select laterResponse contains:
products: Array of matching products with product_id, name, description, pricing_optionsformat_options[] and targeting capabilitiesoverlay_support: binding product-scoped dimensions selectable latertargeting_resolution.modifications: sparse differences from the requested structured overlay; selecting the product accepts themtargeting_resolution.brief_targeting: the seller's single structured interpretation of hard targeting inferred from prosetargeting_resolution means exact acceptance of the structured overlay only; it does not prove how prose was interpretedTreat product_id as the opaque identity of this configured offer. Keep it
within the same discovery/refinement context and purchase it before
expires_at; do not assume it is a permanent cross-session ID.
Create an advertising campaign from selected products.
Request:
{
"brand": {
"domain": "acme.com"
},
"packages": [
{
"product_id": "prod_configured_us_18_44",
"pricing_option_id": "cpm-standard",
"budget": 10000,
"targeting_overlay": {
"geo_metros": [
{ "system": "nielsen_dma", "values": ["501"] }
]
}
}
],
"start_time": "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 responsepricing_option_id: From product's pricing_optionsbudget: Amount in dollarsbid_price: Required for auction pricingtargeting_overlay: Package targeting permitted by the selected product's overlay_support; it composes with targeting already bound during discovery and must not silently broaden itcreative_ids or creatives: Creative assignmentsstart_time (string, required): "asap" or an ISO 8601 datetime (e.g., "2024-06-01T00:00:00Z")end_time (string, required): ISO 8601 datetimeResponse contains:
media_buy_id: The created campaign identifierstatus: Current lifecycle state โ pending_creatives (no creatives assigned yet), pending_start (waiting for flight date), or active (serving immediately)packages: Created packages with their IDsModify an existing campaign.
Request:
{
"idempotency_key": "update-mb-abc123-2024-04-pause",
"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 updateupdates (object): Changes to apply - budget_change, end_time, status, targeting, etc.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:
{
"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, appitems (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, customdelete_missing (boolean, optional): Remove catalogs not in this sync (use with caution)dry_run (boolean, optional): Preview changes without applyingUpload and manage creative assets.
Request:
{
"creatives": [
{
"creative_id": "hero_video_30s",
"name": "Brand Hero Video",
"format_kind": "video_hosted",
"format_option_ref": {
"scope": "product",
"format_option_id": "video_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 synccreative_id: Your unique identifierformat_kind: Canonical format accepted by the selected productformat_option_ref: Product or publisher option when format_kind alone is ambiguousassets: Asset content (video, image, html, etc.)assignments (object, optional): Map creative_id to package IDsdry_run (boolean): Preview changes without applyingdelete_missing (boolean): Archive creatives not in this syncQuery the creative library with filtering.
Request:
{
"filters": {
"status": ["active"]
},
"limit": 20
}
Retrieve media buy state: status, valid_actions, creative approvals, pending formats, and optional delivery snapshots or revision history.
Request:
{
"media_buy_ids": ["mb_abc123"],
"include_snapshot": true,
"include_history": 5
}
Key fields:
media_buy_ids (array, optional): Specific media buy IDs to retrieveaccount (object, optional): Filter to a specific accountstatus_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 packageinclude_history (integer, optional): Include the last N revision history entries per media buyResponse contains:
media_buys: Array with media_buy_id, status, valid_actions, packages, creative approval statesnapshot per package (impressions, spend, pacing)history entries (revision, timestamp, actor, action, summary)Before querying indicators, read get_adcp_capabilities.media_buy.supported_indicator_types. Indicators may appear at three levels:
get_media_buys.media_buys[] (budget_constrained)packages[] (creative_diversity_low, audience_saturation, inventory_shortfall_forecast, pacing_risk, budget_constrained)creative_approvals[] and the matching list_creatives.assignments.assigned_packages[] (creative_fatigue, creative_quality_opportunity)indicators omitted means unknown. A present array requires indicator_types_evaluated and indicators_as_of; empty means clear only for those named types and coverage. scope narrows an assertion; indicators_evaluated_scope declares partial publisher/placement coverage. Creative-library sellers advertise list_creatives in relationship_notifications.projection_tasks; those sellers include media_buy_id, approval state, and any approval_scopes on every reverse assignment row. Every seller repairs through get_media_buys.
For portfolio discovery, call list_creatives with filters.indicator_types, include_assignments: true, assignment_projection: "matching", a bounded assignment_limit, fields: ["creative_id", "assignments"], and cursor pagination. The seller still returns the released required creative envelope; fields limits optional payload. Check assignments_truncated; use get_media_buys for complete repair. Key evaluated state by seller + media_buy_id + package_id + creative_id + type + normalized placement scope.
Never clear from filtered disappearance or failure. Reread directly without indicator_types; clear only a named evaluated type in covered scope from a strictly newer snapshot. Equal-timestamp conflicts are no-ops. Direct assignment deletion retires its keys.
Indicator polling through get_media_buys does not require webhooks. Sellers may additionally declare indicators.changed and may independently declare creative.assignment_changed; creative-library sellers may advertise the bounded list_creatives reverse projection. Subscriptions are prospective, so establish a complete get_media_buys baseline after activation by enumerating known IDs or requesting every media-buy status and exhausting pagination, without indicator_types. Verify, dedupe, and reread get_media_buys; webhook payloads are invalidations, not state. Timestamp-only reevaluation does not fire, while material in-place creative updates invalidate prior assignment evaluations. Root warnings[] on completed buy_products, accept_proposal, or control_media_buy calls are immediate receipts; create_media_buy and update_media_buy facades mirror them. Inventory and pacing warning codes require the matching advertised durable indicator type.
Submit one compact optimizer-ready assertion. Measurement agents call a buyer-controlled orchestrator gateway; the orchestrator authenticates and normalizes provider output, then calls each seller under the buyer's identity. Measurement providers do not receive seller-account grants.
Request:
{
"idempotency_key": "feedback-mb-abc123-2025-01-final",
"media_buy_id": "mb_abc123",
"measurement_period": {
"start": "2025-01-01T00:00:00Z",
"end": "2025-01-31T23:59:59Z"
},
"performance_index": 1.2,
"baseline": "campaign_target",
"metric": {
"scope": "standard",
"metric_id": "conversions"
},
"producer": { "domain": "pinnacle-measurement.example" },
"methodology": "deterministic_attribution",
"final": true
}
Key fields:
idempotency_key (string, required): Stable key for this logical assertion; retries reuse the same key and payloadmedia_buy_id (string, required): Publisher's media buy identifiermeasurement_period (object, required): Time period with start and end (ISO 8601)performance_index (number, required): Normalized score โ 1.0 equals baseline, lower underperforms, higher outperforms. Use observed/baseline for higher-is-better ratios and baseline/observed for lower-is-better ratios such as CPA.baseline (string, required for compact-contract producers): campaign_target, control_group, seller_history, buyer_portfolio, market_benchmark, or otherpackage_id (string, optional): Specific package for package-level feedbackcreative_id (string, optional): Specific creative for creative-level feedbackmetric (object, optional): Standard/vendor metric identity; preferred over deprecated metric_typeproducer (BrandRef, conditionally required): Measurement provider that produced the analysis; required when methodology or methodology_version is present. The orchestrator verifies it against provider identity before preserving it on seller submissionsmethodology, methodology_version (string, optional): Provider-scoped open identifiersstudy_ref (string, optional): Opaque correlation reference, never an experiment-execution instructionevidence / evidence_ref (optional): Small inline summary and provider-hosted detailfinal, as_of, supersedes_feedback_id (optional): Maturation and immutable revision fieldsSellers declaring media_buy.performance_feedback also list measurement.core in top-level experimental_features and return feedback_id. When reports_application_status is true, inspect application_status: accepted is not an application claim; applied means the signal entered optimizer inputs; not_applied includes a reason. Do not confuse this with the response envelope's task status.
Do not send raw measurement datasets through this task or through report_usage. In the first gateway tier the provider reads delivery through the orchestrator's get_media_buy_delivery task and returns only the compact decision signal through provide_performance_feedback.
Retrieve performance metrics for a campaign.
Request:
{
"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 packagetimeseries: Data points over time if granularity specifiedBrand context is provided by domain reference:
{
"brand": {
"domain": "acmecorp.com"
}
}
The agent resolves the domain to retrieve the brand's identity (name, colors, guidelines, etc.) from its brand.json file.
Products declare their closed accepted set directly:
{
"format_option_id": "display_image_300x250",
"format_kind": "image",
"params": { "width": 300, "height": 250 }
}
Buyers select the option with format_option_refs[] on the package and submit a manifest using format_kind plus format_option_ref. Compound named format IDs are deprecated in 3.2.
Products include pricing_options array. Each option has:
pricing_option_id: Use this in create_media_buypricing_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.
Operations like create_media_buy and sync_creatives may require human approval. The response includes:
status: "pending" - Operation awaiting approvaltask_id - For tracking async progressPoll or use webhooks to check completion status.
Common error patterns:
Error responses include:
{
"errors": [
{
"code": "VALIDATION_ERROR",
"message": "budget must be greater than 0",
"field": "packages[0].budget"
}
]
}
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:
{
"account": {
"sandbox": true
}
}
To enter sandbox mode, set sandbox: true on the account reference:
{
"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 for full details.