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,
oneOfvariants, asyncstatus:'submitted'polling, error recovery fromadcp_error.issues[]— live inskills/call-adcp-agent/SKILL.md. This skill covers per-task semantics only.
Overview
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 |
Typical Workflow
- Discover products: use
list_products, orrequest_proposalswith a brief - Verify the offer: inspect formats, pricing, forecast,
overlay_support, and anytargeting_resolution - Negotiate and hold: revise a draft as needed, then finalize it without changing terms
- Accept or decline: call
accept_proposalfor the held snapshot ordecline_proposalswhen the buyer stops pursuing it - Upload creatives: use
sync_creativesto add creative assets - Monitor delivery: use
get_media_buy_deliveryto track performance
Canonical formats (AdCP 3.2)
Products 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, orcoordinated_placements; usecustomonly withformat_shapeandformat_schemaparams— per-canonical parameters narrowing the format (dimensions, durations, codecs, char limits, CTA enums)- Optional
format_option_id— disambiguates product options and identifies publisher-catalog declarations when paired withpublisher_domain - Optional
v1_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 size - Optional
seller_preference: "preferred" | "accepted" | "discouraged"— soft routing hint when a multi-format product has several options at the same price
Multi-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.
Task Reference
get_products
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 inbrief,wholesale, andrefine.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 later
Response contains:
products: Array of matching products withproduct_id,name,description,pricing_options- Each product includes canonical
format_options[]and targeting capabilities overlay_support: binding product-scoped dimensions selectable latertargeting_resolution.modifications: sparse differences from the requested structured overlay; selecting the product accepts them- Response
targeting_resolution.brief_targeting: the seller's single structured interpretation of hard targeting inferred from prose - No
targeting_resolutionmeans exact acceptance of the structured overlay only; it does not prove how prose was interpreted
Treat 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_media_buy
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: Fromget_productsresponsepricing_option_id: From product'spricing_optionsbudget: Amount in dollarsbid_price: Required for auction pricingtargeting_overlay: Package targeting permitted by the selected product'soverlay_support; it composes with targeting already bound during discovery and must not silently broaden itcreative_idsorcreatives: Creative assignments
start_time(string, required):"asap"or an ISO 8601 datetime (e.g.,"2024-06-01T00:00:00Z")end_time(string, required): ISO 8601 datetime
Response contains:
media_buy_id: The created campaign identifierstatus: Current lifecycle state —pending_creatives(no creatives assigned yet),pending_start(waiting for flight date), oractive(serving immediately)packages: Created packages with their IDs
update_media_buy
Modify 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_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:
{
"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 withurl)url(string): External feed URL (mutually exclusive withitems)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:
{
"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 whenformat_kindalone 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 sync
list_creatives
Query the creative library with filtering.
Request:
{
"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:
{
"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 buy
Response contains:
media_buys: Array withmedia_buy_id,status,valid_actions,packages, creative approval state- Optional
snapshotper package (impressions, spend, pacing) - Optional
historyentries (revision, timestamp, actor, action, summary)
Relationship-scoped indicators
Before querying indicators, read get_adcp_capabilities.media_buy.supported_indicator_types. Indicators may appear at three levels:
- buy:
get_media_buys.media_buys[](budget_constrained) - package:
packages[](creative_diversity_low,audience_saturation,inventory_shortfall_forecast,pacing_risk,budget_constrained) - assignment:
creative_approvals[]and the matchinglist_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.
provide_performance_feedback
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 withstartandend(ISO 8601)performance_index(number, required): Normalized score — 1.0 equalsbaseline, 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, orotherpackage_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 deprecatedmetric_typeproducer(BrandRef, conditionally required): Measurement provider that produced the analysis; required whenmethodologyormethodology_versionis 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 fields
Sellers 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.
get_media_buy_delivery
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 specified
Key Concepts
Brand identity
Brand 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.
Canonical format options
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.
Pricing Options
Products include pricing_options array. Each option has:
pricing_option_id: Use this increate_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.
Asynchronous Operations
Operations like create_media_buy and sync_creatives may require human approval. The response includes:
status: "pending"- Operation awaiting approvaltask_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:
{
"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:
{
"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.
微信扫一扫