Skip to main content

create_media_buy

Create a media buy from selected packages. This task handles the complete workflow including validation, approval if needed, and campaign creation. Response Time: Instant to days (returns completed, working < 120s, or submitted for hours/days) Pricing & Currency: Each package specifies its own pricing_option_id, which determines currency, pricing model (CPM, CPCV, CPP, etc.), and rates. Packages can use different currencies when sellers support it—sellers validate and reject incompatible combinations. See Pricing Models for details. Format Specification Required: Each package must specify the creative formats that will be used. This enables placeholder creation in ad servers and ensures both parties have clear expectations for creative asset requirements. Request Schema: /schemas/v1/media-buy/create-media-buy-request.json Response Schema: /schemas/v1/media-buy/create-media-buy-response.json

Request Parameters

ParameterTypeRequiredDescription
buyer_refstringYesBuyer’s reference identifier for this media buy
packagesPackage[]YesArray of package configurations (see Package Object below)
brand_manifestBrandManifestRefYesBrand information identifying the advertiser. Required so publishers know who they’re doing business with for policy compliance and business purposes. Minimal manifests (just name/URL) are acceptable for sales agents - additional details like colors, fonts, and assets are only needed for creative generation and optimization. Can be provided as an inline object or URL reference to a hosted manifest. Can be cached and reused across multiple requests. See Brand Manifest for details and minimal usage patterns.
promoted_productsPromotedProductsNoProducts or offerings being promoted in this media buy. Useful for campaign-level reporting, policy compliance, and publisher understanding of what’s being advertised. Selects from brand manifest’s product catalog using SKUs, tags, categories, or natural language queries.
po_numberstringNoPurchase order number for tracking
start_timestringYesCampaign start time: "asap" to start as soon as possible, or ISO 8601 date-time for scheduled start
end_timestringYesCampaign end date/time in ISO 8601 format (UTC unless timezone specified)
reporting_webhookReportingWebhookNoOptional webhook configuration for automated reporting delivery (see Reporting Webhook Object below)

Package Object

ParameterTypeRequiredDescription
buyer_refstringYesBuyer’s reference identifier for this package
product_idstringYesProduct ID for this package
pricing_option_idstringYesPricing option ID from the product’s pricing_options array - specifies pricing model and currency for this package. See Pricing Models for details.
format_idsFormatID[]YesArray of structured format ID objects that will be used for this package - must be supported by the product
budgetnumberYesBudget allocation for this package in the currency specified by the pricing option
pacingstringNoPacing strategy: "even" (default), "asap", or "front_loaded"
bid_pricenumberNoBid price for auction-based pricing options (required when pricing_option.is_fixed is false)
targeting_overlayTargetingOverlayNoAdditional targeting criteria for this package (see Targeting Overlay Object below)
creative_idsstring[]NoCreative IDs to assign to this package at creation time (references existing library creatives)
creativesCreativeAsset[]NoFull creative objects to upload and assign to this package at creation time (alternative to creative_ids - creatives will be added to library). Supports both static and generative creatives. Max 100 per package.

Targeting Overlay Object

Note: Targeting overlays should be rare. Most targeting should be expressed in your brief and handled by the publisher through product selection. Use overlays only for geographic restrictions (RCT testing, regulatory compliance) or frequency capping.
ParameterTypeRequiredDescription
geo_country_any_ofstring[]NoRestrict delivery to specific countries (ISO codes). Use for regulatory compliance or RCT testing.
geo_region_any_ofstring[]NoRestrict delivery to specific regions/states. Use for regulatory compliance or RCT testing.
geo_metro_any_ofstring[]NoRestrict delivery to specific metro areas (DMA codes). Use for regulatory compliance or RCT testing.
geo_postal_code_any_ofstring[]NoRestrict delivery to specific postal/ZIP codes. Use for regulatory compliance or RCT testing.
axe_include_segmentstringNoAXE segment ID to include for targeting
axe_exclude_segmentstringNoAXE segment ID to exclude from targeting
frequency_capFrequencyCapNoFrequency capping settings (see Frequency Cap Object below)

Frequency Cap Object

ParameterTypeRequiredDescription
suppress_minutesnumberYesMinutes to suppress after impression (applied at package level)

Reporting Webhook Object

ParameterTypeRequiredDescription
urlstringYesWebhook endpoint URL for reporting notifications
auth_typestringYesAuthentication type: "bearer", "basic", or "none"
auth_tokenstringNo*Authentication token or credentials (required unless auth_type is “none”)
reporting_frequencystringYesReporting frequency: "hourly", "daily", or "monthly". Must be supported by all products in the media buy.
requested_metricsstring[]NoOptional list of metrics to include in webhook notifications. If omitted, all available metrics are included. Must be subset of product’s available_metrics.
Publisher Commitment: When a reporting webhook is configured, the publisher commits to sending (campaign_duration / reporting_frequency) + 1 webhook notifications:
  • One notification per frequency period during the campaign
  • One final notification when the campaign completes
  • If reporting data is delayed beyond the product’s expected_delay_minutes, a notification with "delayed" status will be sent to avoid appearing as a missed notification
Timezone Considerations: For daily and monthly frequencies, the publisher’s reporting timezone (specified in reporting_capabilities.timezone) determines when periods begin/end. Ensure alignment between your systems and the publisher’s timezone to avoid confusion about reporting period boundaries.

Response (Message)

The response includes a human-readable message that:
  • Confirms the media buy was created with budget and targeting details
  • Explains next steps and deadlines
  • Describes any approval requirements
  • Provides implementation details and status updates
The message is returned differently in each protocol:
  • MCP: Returned as a message field in the JSON response
  • A2A: Returned as a text part in the artifact

Response (Payload)

{
  "media_buy_id": "string",
  "buyer_ref": "string",
  "creative_deadline": "string",
  "packages": [
    {
      "package_id": "string",
      "buyer_ref": "string"
    }
  ]
}

Field Descriptions

  • media_buy_id: Publisher’s unique identifier for the created media buy
  • buyer_ref: Buyer’s reference identifier for this media buy
  • creative_deadline: ISO 8601 timestamp for creative upload deadline
  • packages: Array of created packages
    • package_id: Publisher’s unique identifier for the package
    • buyer_ref: Buyer’s reference identifier for the package

Protocol-Specific Examples

The AdCP payload is identical across protocols. Only the request/response wrapper differs.

MCP Request

{
  "tool": "create_media_buy",
  "arguments": {
    "buyer_ref": "nike_q1_campaign_2024",
    "packages": [
      {
        "buyer_ref": "nike_ctv_sports_package",
        "product_id": "ctv_sports_premium",
        "format_ids": [
          {
            "agent_url": "https://creatives.adcontextprotocol.org",
            "id": "video_standard_30s"
          },
          {
            "agent_url": "https://creatives.adcontextprotocol.org",
            "id": "video_standard_15s"
          }
        ],
        "budget": 60000,
        "pacing": "even",
        "pricing_option_id": "cpm-fixed-sports",
        "targeting_overlay": {
          "geo_country_any_of": ["US"],
          "geo_region_any_of": ["CA", "NY"],
          "axe_include_segment": "x8dj3k"
        },
        "creative_ids": ["creative_abc123", "creative_def456"]
      },
      {
        "buyer_ref": "nike_audio_drive_package",
        "product_id": "audio_drive_time",
        "format_ids": [
          {
            "agent_url": "https://creatives.adcontextprotocol.org",
            "id": "audio_standard_30s"
          }
        ],
        "budget": 40000,
        "pacing": "front_loaded",
        "pricing_option_id": "cpm-fixed-audio",
        "targeting_overlay": {
          "geo_country_any_of": ["US"],
          "geo_region_any_of": ["CA"],
          "axe_exclude_segment": "x9m2p"
        }
      }
    ],
    "promoted_offering": "Nike Air Max 2024 - premium running shoes",
    "po_number": "PO-2024-Q1-001",
    "start_time": "2024-02-01T00:00:00Z",
    "end_time": "2024-03-31T23:59:59Z",
    "reporting_webhook": {
      "url": "https://buyer.example.com/webhooks/reporting",
      "auth_type": "bearer",
      "auth_token": "secret_reporting_token_xyz",
      "reporting_frequency": "daily",
      "requested_metrics": ["impressions", "spend", "video_completions", "completion_rate"]
    }
  }
}

MCP Response (Synchronous)

{
  "message": "Successfully created $100,000 media buy. Upload creatives by Jan 30. Campaign will run from Feb 1 to Mar 31.",
  "status": "completed",
  "media_buy_id": "mb_12345",
  "buyer_ref": "nike_q1_campaign_2024",
  "creative_deadline": "2024-01-30T23:59:59Z",
  "packages": [
    {
      "package_id": "pkg_12345_001",
      "buyer_ref": "nike_ctv_sports_package"
    },
    {
      "package_id": "pkg_12345_002",
      "buyer_ref": "nike_audio_drive_package"
    }
  ]
}

MCP Response (Partial Success with Errors)

{
  "message": "Media buy created but some packages had issues. Review targeting for best performance.",
  "media_buy_id": "mb_12346",
  "buyer_ref": "nike_q1_campaign_2024",
  "creative_deadline": "2024-01-30T23:59:59Z",
  "packages": [
    {
      "package_id": "pkg_12346_001",
      "buyer_ref": "nike_ctv_sports_package"
    }
  ],
  "errors": [
    {
      "code": "TARGETING_TOO_NARROW",
      "message": "Package targeting yielded 0 available impressions",
      "field": "packages[1].targeting_overlay",
      "suggestion": "Broaden geographic targeting or remove segment exclusions",
      "details": {
        "requested_budget": 40000,
        "available_impressions": 0,
        "affected_package": "nike_audio_drive_package"
      }
    }
  ]
}

MCP Response (Asynchronous)

{
  "task_id": "task_456",
  "status": "working",
  "message": "Creating media buy...",
  "poll_url": "/tasks/task_456"
}

A2A Request

Natural Language Invocation

await a2a.send({
  message: {
    parts: [{
      kind: "text",
      text: "Create a $100K Nike campaign from Feb 1 to Mar 31. Use the CTV sports and audio drive time products we discussed. Split budget 60/40."
    }]
  }
});

Explicit Skill Invocation

await a2a.send({
  message: {
    parts: [
      {
        kind: "text",
        text: "Creating Nike Q1 campaign"  // Optional context
      },
      {
        kind: "data",
        data: {
          skill: "create_media_buy",  // Must match skill name in Agent Card
          parameters: {
            "buyer_ref": "nike_q1_campaign_2024",
            "packages": [
              {
                "buyer_ref": "nike_ctv_sports_package",
                "product_id": "ctv_sports_premium",
                "format_ids": [
                  {
                    "agent_url": "https://creatives.adcontextprotocol.org",
                    "id": "video_standard_30s"
                  },
                  {
                    "agent_url": "https://creatives.adcontextprotocol.org",
                    "id": "video_standard_15s"
                  }
                ],
                "budget": 60000,
                "pacing": "even",
                "pricing_option_id": "cpm-fixed-sports",
                "targeting_overlay": {
                  "geo_country_any_of": ["US"],
                  "geo_region_any_of": ["CA", "NY"],
                  "axe_include_segment": "x8dj3k"
                },
                "creative_ids": ["creative_abc123", "creative_def456"]
              },
              {
                "buyer_ref": "nike_audio_drive_package",
                "product_id": "audio_drive_time",
                "format_ids": [
                  {
                    "agent_url": "https://creatives.adcontextprotocol.org",
                    "id": "audio_standard_30s"
                  }
                ],
                "budget": 40000,
                "pacing": "front_loaded",
                "pricing_option_id": "cpm-fixed-audio",
                "targeting_overlay": {
                  "geo_country_any_of": ["US"],
                  "geo_region_any_of": ["CA"],
                  "axe_exclude_segment": "x9m2p"
                }
              }
            ],
            "promoted_offering": "Nike Air Max 2024 - premium running shoes",
            "po_number": "PO-2024-Q1-001",
            "start_time": "2024-02-01T00:00:00Z",
            "end_time": "2024-03-31T23:59:59Z"
          }
        }
      }
    ]
  }
});

A2A Response (with streaming)

Initial response:
{
  "taskId": "task-mb-001",
  "status": { "state": "working" }
}
Then via Server-Sent Events:
data: {"message": "Validating packages..."}
data: {"message": "Checking inventory availability..."}
data: {"message": "Creating campaign in ad server..."}
data: {"status": {"state": "completed"}, "artifacts": [{
  "name": "media_buy_confirmation",
  "parts": [
    {"kind": "text", "text": "Successfully created $100,000 media buy. Upload creatives by Jan 30."},
    {"kind": "data", "data": {
      "media_buy_id": "mb_12345",
      "buyer_ref": "nike_q1_campaign_2024",
      "creative_deadline": "2024-01-30T23:59:59Z",
      "packages": [
        {"package_id": "pkg_12345_001", "buyer_ref": "nike_ctv_sports_package"},
        {"package_id": "pkg_12345_002", "buyer_ref": "nike_audio_drive_package"}
      ]
    }}
  ]
}]}

Key Differences

  • MCP: May return synchronously or asynchronously with updates via:
    • Polling (calling status endpoints)
    • Webhooks (push notifications to callback URLs)
    • Streaming (WebSockets or SSE)
  • A2A: Always returns task with updates via:
    • Server-Sent Events (SSE) for real-time streaming
    • Webhooks (push notifications) for long-running tasks
  • Payload: The input field in A2A contains the exact same structure as MCP’s arguments

Human-in-the-Loop Examples

MCP with Manual Approval (Polling Example)

This example shows polling, but MCP implementations may also support webhooks or streaming for real-time updates. Initial Request:
{
  "tool": "create_media_buy",
  "arguments": {
    "buyer_ref": "large_campaign_2024",
    "packages": [...],
    "promoted_offering": "High-value campaign requiring approval",
    "po_number": "PO-2024-LARGE-001",
    "start_time": "2024-02-01T00:00:00Z",
    "end_time": "2024-06-30T23:59:59Z"
  }
}
Response (Asynchronous):
{
  "task_id": "task_456",
  "status": "working",
  "message": "Large budget requires sales team approval. Expected review time: 2-4 hours.",
  "context_id": "ctx-mb-456"
}
Client checks status (via polling in this example):
{
  "tool": "create_media_buy_status",
  "arguments": {
    "context_id": "ctx-mb-456"
  }
}
Status Response (Still pending):
{
  "status": "working",
  "message": "Awaiting manual approval. Sales team reviewing. 1 of 2 approvals received.",
  "context_id": "ctx-mb-456",
  "responsible_party": "publisher",
  "estimated_completion": "2024-01-15T16:00:00Z"
}
Status Response (Approved):
{
  "status": "completed",
  "message": "Media buy approved and created. Upload creatives by Jan 30.",
  "media_buy_id": "mb_789456",
  "buyer_ref": "large_campaign_2024",
  "creative_deadline": "2024-01-30T23:59:59Z",
  "packages": [...]
}

A2A with Manual Approval (SSE Example)

A2A can use Server-Sent Events for real-time streaming or webhooks for push notifications. Initial Request with SSE:
{
  "skill": "create_media_buy",
  "input": {
    "buyer_ref": "large_campaign_2024",
    "packages": [...],
    "promoted_offering": "High-value campaign requiring approval",
    "po_number": "PO-2024-LARGE-001",
    "start_time": "2024-02-01T00:00:00Z",
    "end_time": "2024-06-30T23:59:59Z"
  }
}
Initial Response:
{
  "taskId": "task-mb-large-001",
  "contextId": "ctx-conversation-xyz",
  "status": { 
    "state": "working",
    "message": "Large budget requires sales team approval"
  }
}
SSE Updates (Human approval process):
data: {"message": "Validating campaign requirements..."}
data: {"message": "Budget exceeds auto-approval threshold. Routing to sales team..."}
data: {"message": "Sales team notified. Expected review time: 2-4 hours"}
data: {"message": "First approval received from regional manager"}
data: {"message": "Second approval received from finance team"}
data: {"status": {"state": "completed"}, "artifacts": [{
  "artifactId": "artifact-mb-large-xyz",
  "name": "media_buy_confirmation",
  "parts": [
    {"kind": "text", "text": "Media buy approved and created successfully. $500,000 campaign scheduled Feb 1 - Jun 30. Upload creatives by Jan 30."},
    {"kind": "data", "data": {
      "media_buy_id": "mb_789456",
      "buyer_ref": "large_campaign_2024",
      "creative_deadline": "2024-01-30T23:59:59Z",
      "packages": [...]
    }}
  ]
}]}

A2A with Webhooks (Long-Running Task)

Initial Request with Webhook Configuration:
{
  "skill": "create_media_buy",
  "input": {
    "buyer_ref": "large_campaign_2024",
    "packages": [...],
    "promoted_offering": "High-value campaign requiring approval",
    "po_number": "PO-2024-LARGE-001",
    "start_time": "2024-02-01T00:00:00Z",
    "end_time": "2024-06-30T23:59:59Z"
  },
  "pushNotificationConfig": {
    "url": "https://buyer.example.com/webhooks/adcp",
    "authType": "bearer",
    "authToken": "secret-token-xyz"
  }
}
Initial Response:
{
  "taskId": "task-mb-webhook-001",
  "contextId": "ctx-conversation-xyz",
  "status": { 
    "state": "working",
    "message": "Task processing. Updates will be sent to webhook."
  }
}
Webhook Notifications (sent to buyer’s endpoint):
// First webhook
{
  "taskId": "task-mb-webhook-001",
  "contextId": "ctx-conversation-xyz",
  "status": {"state": "working"},
  "message": "Budget exceeds threshold. Awaiting sales approval."
}

// Final webhook when complete
{
  "taskId": "task-mb-webhook-001",
  "contextId": "ctx-conversation-xyz",
  "status": {"state": "completed"},
  "artifacts": [{
    "artifactId": "artifact-mb-webhook-xyz",
    "name": "media_buy_confirmation",
    "parts": [
      {"kind": "data", "data": {
        "media_buy_id": "mb_789456",
        "buyer_ref": "large_campaign_2024",
        "creative_deadline": "2024-01-30T23:59:59Z",
        "packages": [...]
      }}
    ]
  }]
}

A2A with Input Required

If the system needs clarification (e.g., ambiguous targeting): SSE Update requesting input:
data: {"status": {"state": "input-required", "message": "Multiple interpretations found for 'sports fans'. Please specify: 1) All sports enthusiasts, 2) NFL fans specifically, 3) Live sports event viewers"}}
Client provides clarification:
{
  "referenceTaskIds": ["task-mb-large-001"],
  "message": "Target all sports enthusiasts including NFL, NBA, and soccer fans"
}
Task resumes with clarification:
data: {"status": {"state": "working", "message": "Applying targeting for all sports enthusiasts..."}}
data: {"status": {"state": "completed"}, "artifacts": [...]}

Scenarios

Media Buy with Inline Creatives (Single Atomic Operation)

Create a media buy and upload creatives in a single API call. This eliminates the need for a separate sync_creatives call and ensures creatives and campaign are created together atomically.
{
  "buyer_ref": "nike_q1_campaign_2024",
  "brand_manifest": {
    "brand": {
      "name": "Nike",
      "website": "https://nike.com"
    },
    "products": [
      {
        "sku": "AIR_MAX_2024",
        "name": "Air Max 2024",
        "category": "athletic footwear",
        "price": {"amount": 150, "currency": "USD"}
      }
    ]
  },
  "promoted_products": {
    "skus": ["AIR_MAX_2024"]
  },
  "packages": [
    {
      "buyer_ref": "nike_ctv_package",
      "product_id": "ctv_sports_premium",
      "format_ids": [
        {
          "agent_url": "https://creatives.adcontextprotocol.org",
          "id": "video_standard_30s"
        }
      ],
      "budget": 50000,
      "pricing_option_id": "cpm-fixed-sports",
      "creatives": [
        {
          "creative_id": "hero_video_30s",
          "name": "Air Max Hero Video",
          "format_id": {
            "agent_url": "https://creative.adcontextprotocol.org",
            "id": "video_standard_30s"
          },
          "assets": {
            "video": {
              "asset_type": "video",
              "url": "https://cdn.nike.com/hero-30s.mp4",
              "width": 1920,
              "height": 1080,
              "duration_ms": 30000
            }
          },
          "tags": ["q1_2024", "hero"]
        }
      ]
    },
    {
      "buyer_ref": "nike_display_package",
      "product_id": "display_premium",
      "format_ids": [
        {
          "agent_url": "https://creatives.adcontextprotocol.org",
          "id": "premium_bespoke_display"
        }
      ],
      "budget": 30000,
      "pricing_option_id": "cpm-fixed-display",
      "creatives": [
        {
          "creative_id": "holiday_hero_display",
          "name": "Air Max Holiday Display (Generative)",
          "format_id": {
            "agent_url": "https://publisher.com/.well-known/adcp/sales",
            "id": "premium_bespoke_display"
          },
          "assets": {
            "promoted_offerings": {
              "asset_type": "promoted_offerings",
              "url": "https://nike.com/air-max",
              "colors": {
                "primary": "#111111",
                "secondary": "#FFFFFF"
              }
            },
            "generation_prompt": {
              "asset_type": "text",
              "content": "Create a bold, athletic display ad emphasizing innovation and performance. Target runners aged 25-45."
            }
          },
          "tags": ["q1_2024", "generative"]
        }
      ]
    }
  ],
  "start_time": "asap",
  "end_time": "2024-03-31T23:59:59Z"
}
Benefits:
  • Single API call - Creates media buy and uploads both static and generative creatives atomically
  • Simplified workflow - No need to manage creative library separately for new campaigns
  • Atomic operation - If media buy fails, creatives aren’t orphaned in library
  • Mixed creative types - Combine static assets (video) with generative formats (display) in same request
  • Brand context - Generative creatives leverage brand_manifest for creation
Note: Creatives are still added to the library for reuse. Use creative_ids to reference existing library creatives, or creatives to upload new ones.

Standard Media Buy Request

{
  "buyer_ref": "purina_pet_campaign_q1",
  "packages": [
    {
      "buyer_ref": "purina_ctv_package",
      "product_id": "ctv_prime_time",
      "format_ids": [
        {
          "agent_url": "https://creatives.adcontextprotocol.org",
          "id": "video_standard_30s"
        }
      ],
      "budget": 30000,
      "pacing": "even",
      "pricing_option_id": "cpm-fixed-ctv",
      "targeting_overlay": {
        "geo_country_any_of": ["US"],
        "geo_region_any_of": ["CA", "NY"],
        "axe_include_segment": "x7h4n",
        "signals": ["auto_intenders_q1_2025"],
        "frequency_cap": {
          "suppress_minutes": 30
        }
      }
    },
    {
      "buyer_ref": "purina_audio_package",
      "product_id": "audio_drive_time",
      "format_ids": [
        {
          "agent_url": "https://creatives.adcontextprotocol.org",
          "id": "audio_standard_30s"
        }
      ],
      "budget": 20000,
      "pricing_option_id": "cpm-fixed-audio",
      "targeting_overlay": {
        "geo_country_any_of": ["US"],
        "geo_region_any_of": ["CA", "NY"]
      }
    }
  ],
  "promoted_offering": "Purina Pro Plan dog food - premium nutrition tailored for dogs' specific needs, promoting the new salmon and rice formula for sensitive skin and stomachs",
  "po_number": "PO-2024-Q1-0123",
  "start_time": "2024-02-01T00:00:00Z",
  "end_time": "2024-02-29T23:59:59Z"
}

Retail Media Buy Request

{
  "buyer_ref": "purina_albertsons_retail_q1",
  "packages": [
    {
      "buyer_ref": "purina_albertsons_conquest",
      "product_id": "albertsons_competitive_conquest",
      "format_ids": [
        {
          "agent_url": "https://creatives.adcontextprotocol.org",
          "id": "display_300x250"
        },
        {
          "agent_url": "https://creatives.adcontextprotocol.org",
          "id": "display_728x90"
        }
      ],
      "budget": 75000,
      "pacing": "even",
      "pricing_option_id": "cpm-fixed-retail",
      "targeting_overlay": {
        "geo_country_any_of": ["US"],
        "geo_region_any_of": ["CA", "AZ", "NV"],
        "axe_include_segment": "x3f9q",
        "axe_exclude_segment": "x2v8r",
        "frequency_cap": {
          "suppress_minutes": 60
        }
      }
    }
  ],
  "promoted_offering": "Purina Pro Plan dog food - premium nutrition tailored for dogs' specific needs",
  "po_number": "PO-2024-RETAIL-0456",
  "start_time": "2024-02-01T00:00:00Z",
  "end_time": "2024-03-31T23:59:59Z"
}

Response - Success

Message: “Successfully created your $50,000 media buy targeting pet owners in CA and NY. The campaign will reach 2.5M users through Connected TV and Audio channels. Please upload creative assets by January 30 to activate the campaign. Campaign scheduled to run Feb 1-29.” Payload:
{
  "media_buy_id": "gam_1234567890",
  "buyer_ref": "purina_pet_campaign_q1",
  "creative_deadline": "2024-01-30T23:59:59Z",
  "packages": [
    {
      "package_id": "gam_pkg_001",
      "buyer_ref": "purina_ctv_package"
    },
    {
      "package_id": "gam_pkg_002",
      "buyer_ref": "purina_audio_package"
    }
  ]
}

Response - Retail Media Success

Message: “Successfully created your $75,000 retail media campaign targeting competitive dog food buyers. The campaign will reach 450K Albertsons shoppers with deterministic purchase data. Creative assets must include co-branding and drive to Albertsons.com. Upload by January 30 to activate. Campaign runs Feb 1 - Mar 31.” Payload:
{
  "media_buy_id": "albertsons_mb_789012",
  "buyer_ref": "purina_albertsons_retail_q1",
  "creative_deadline": "2024-01-30T23:59:59Z",
  "packages": [
    {
      "package_id": "albertsons_pkg_001",
      "buyer_ref": "purina_albertsons_conquest"
    }
  ]
}

Response - Pending Manual Approval

Message: “Your $50,000 media buy has been submitted for approval. Due to the campaign size, it requires manual review by our sales team. Expected approval time is 2-4 hours during business hours. You’ll receive a notification once approved. Campaign scheduled for Feb 1 - Mar 31.” Payload:
{
  "media_buy_id": "mb_789",
  "buyer_ref": "nike_q1_campaign_2024",
  "creative_deadline": null,
  "packages": []
}

Platform Behavior

Different advertising platforms handle media buy creation differently:
  • Google Ad Manager (GAM): Creates Order with LineItems, requires approval
  • Kevel: Creates Campaign with Flights, instant activation
  • Triton: Creates Campaign for audio delivery

Status Values

Both protocols use standard task states:
  • working: Task is in progress (includes waiting for approvals, processing, etc.)
  • input-required: Needs clarification or additional information from client
  • completed: Task finished successfully
  • failed: Task encountered an error
  • cancelled: Task was cancelled
  • rejected: Task was rejected (e.g., policy violation)
Note: Specific business states (like “awaiting manual approval”, “pending creative assets”, etc.) are conveyed through the message field, not custom status values. This ensures consistency across protocols.

Asynchronous Behavior

This operation can be either synchronous or asynchronous depending on the publisher’s implementation and the complexity of the request.

Synchronous Response

When the operation can be completed immediately (rare), the response includes the created media buy details directly.

Asynchronous Response

When the operation requires processing time, the response returns immediately with:
  • A tracking identifier (context_id for MCP, taskId for A2A)
  • Initial status ("working" for both MCP and A2A)
  • Updates can be received via:
    • Polling: Call status endpoints periodically (MCP and A2A)
    • Webhooks: Register callback URLs for push notifications (MCP and A2A)
    • Streaming: Use SSE or WebSockets for real-time updates (MCP and A2A)

Status Checking

MCP Status Checking

Option 1: Polling (create_media_buy_status)

For MCP implementations using polling, use this endpoint to check the status of an asynchronous media buy creation.

Request

{
  "context_id": "ctx-create-mb-456"  // Required - from create_media_buy response
}

Response Examples

Processing:
{
  "message": "Media buy creation in progress - validating inventory",
  "context_id": "ctx-create-mb-456",
  "status": "working",
  "progress": {
    "current_step": "inventory_validation",
    "completed": 2,
    "total": 5,
    "unit_type": "steps",
    "responsible_party": "system"
  }
}
Completed:
{
  "message": "Successfully created your $50,000 media buy. Upload creative assets by Jan 30.",
  "context_id": "ctx-create-mb-456",
  "status": "completed",
  "media_buy_id": "gam_1234567890",
  "buyer_ref": "espn_sports_q1_2024",
  "creative_deadline": "2024-01-30T23:59:59Z",
  "packages": [
    {"package_id": "gam_pkg_001", "buyer_ref": "espn_ctv_sports"},
    {"package_id": "gam_pkg_002", "buyer_ref": "espn_audio_sports"}
  ]
}
Pending Manual Approval:
{
  "message": "Media buy requires manual approval. Sales team reviewing campaign.",
  "context_id": "ctx-create-mb-456",
  "status": "working",
  "responsible_party": "publisher",
  "action_detail": "Sales team reviewing campaign"
}

Option 2: Webhooks (MCP)

Register a callback URL to receive push notifications for long-running operations. Webhooks are ONLY used when the initial response is submitted. Configuration:
const response = await session.call('create_media_buy',
  {
    buyer_ref: "campaign_2024",
    packages: [...]
  },
  {
    webhook_url: "https://buyer.example.com/webhooks/adcp/create_media_buy/agent_id/op_id",
    webhook_auth: { type: "bearer", credentials: "bearer-token-xyz" }
  }
);
Response patterns:
  • completed - Synchronous success, webhook NOT called (you have the result)
  • working - Will complete within ~120s, webhook NOT called (wait for response)
  • submitted - Long-running operation, webhook WILL be called on status changes
Example webhook flow (only for submitted operations): Webhook POST for human approval needed:
POST /webhooks/adcp/create_media_buy/agent_id/op_id HTTP/1.1
Host: buyer.example.com
Authorization: Bearer bearer-token-xyz
Content-Type: application/json

{
  "operation_id": "op_id",
  "task_id": "task_456",
  "task_type": "create_media_buy",
  "status": "input-required",
  "message": "Campaign budget $150K requires approval to proceed",
  "result": {
    "buyer_ref": "campaign_2024"
  }
}
Webhook POST when complete (after approval - full create_media_buy response):
POST /webhooks/adcp/create_media_buy/agent_id/op_id HTTP/1.1
Host: buyer.example.com
Authorization: Bearer bearer-token-xyz
Content-Type: application/json

{
  "operation_id": "op_id",
  "task_id": "task_456",
  "task_type": "create_media_buy",
  "status": "completed",
  "result": {
    "media_buy_id": "mb_12345",
    "buyer_ref": "campaign_2024",
    "creative_deadline": "2024-01-30T23:59:59Z",
    "packages": [
      {
        "package_id": "pkg_001",
        "buyer_ref": "ctv_package"
      }
    ]
  }
}
Each webhook includes protocol fields plus a result object for the task-specific payload of that status. See Task Management: Webhook Integration for complete details.

A2A Status Checking

A2A supports both SSE streaming and webhooks as shown in the examples above. Choose based on your needs:
  • SSE: Best for real-time updates with persistent connection
  • Webhooks: Best for long-running tasks or when client may disconnect

Polling Guidelines (when using polling):

  • First 10 seconds: Every 1-2 seconds
  • Next minute: Every 5-10 seconds
  • After 1 minute: Every 30-60 seconds
  • For manual approval (when message indicates approval needed): Every 5 minutes

Handling Pending States

Orchestrators MUST handle pending states as normal operation flow:
  1. Store the context_id for tracking
  2. Monitor for updates via configured method (polling, webhooks, or streaming)
  3. Handle eventual completion, rejection, or manual approval

Example Pending Operation Flow

# 1. Create media buy
response = await mcp.call_tool("create_media_buy", {
    "buyer_ref": "espn_sports_q1_2024",
    "packages": [
        {
            "buyer_ref": "espn_ctv_sports",
            "product_id": "sports_ctv_premium",
            "budget": 30000,
            "pacing": "even",
            "pricing_option_id": "cpm-fixed-sports",
            "targeting_overlay": {
                "geo_country_any_of": ["US"],
                "geo_region_any_of": ["CA", "NY"],
                "axe_include_segment": "x5j7w"
            }
        },
        {
            "buyer_ref": "espn_audio_sports",
            "product_id": "audio_sports_talk",
            "budget": 20000,
            "pricing_option_id": "cpm-fixed-audio",
            "targeting_overlay": {
                "geo_country_any_of": ["US"]
            }
        }
    ],
    "promoted_offering": "ESPN+ streaming service - exclusive UFC fights and soccer leagues, promoting annual subscription",
    "po_number": "PO-2024-001",
    "start_time": "2024-02-01T00:00:00Z",
    "end_time": "2024-03-31T23:59:59Z"
})

# Check if async processing is needed
if response.get("status") == "working":
    context_id = response["context_id"]
    
    # 2. Monitor for completion (polling example shown, but webhooks/streaming may be available)
    while True:
        status_response = await mcp.call_tool("create_media_buy_status", {
            "context_id": context_id
        })
        
        if status_response["status"] == "completed":
            # Operation completed successfully
            media_buy_id = status_response["media_buy_id"]
            break
        elif status_response["status"] == "failed":
            # Operation failed
            handle_error(status_response["error"])
            break
        elif status_response["status"] == "working" and "approval" in status_response.get("message", "").lower():
            # Requires human approval - may take hours/days
            notify_user_of_pending_approval(status_response)
            # Continue polling less frequently
            await sleep(300)  # Check every 5 minutes
        else:
            # Still processing
            await sleep(10)  # Poll every 10 seconds

Platform Mapping

How media buy creation maps to different platforms:
  • Google Ad Manager: Creates an Order with LineItems
  • Kevel: Creates a Campaign with Flights
  • Triton Digital: Creates a Campaign with Flights

Format Workflow and Placeholder Creatives

Why Format Specification is Required

When creating a media buy, format specification serves critical purposes:
  1. Placeholder Creation: Publisher creates placeholder creatives in ad server with correct format specifications
  2. Validation: System validates that selected products actually support the requested formats
  3. Clear Expectations: Both parties know exactly what creative formats are needed
  4. Progress Tracking: Track which creative assets are missing vs. required
  5. Technical Setup: Ad server configuration completed before actual creatives arrive

Workflow Integration

The complete media buy workflow with format awareness:
1. list_creative_formats -> Get available format specifications
2. get_products -> Find products (returns format IDs they support)
3. Validate format compatibility -> Ensure products support desired formats  
4. create_media_buy -> Specify formats for each package (REQUIRED)
   └── Publisher creates placeholders in ad server
   └── Both sides have clear creative requirements
5. sync_creatives -> Upload actual files matching the specified formats
6. Campaign activation -> Replace placeholders with real creatives

Format Validation

Publishers MUST validate that:
  • All specified formats are supported by the product in each package
  • Format specifications match those returned by list_creative_formats
  • Creative requirements can be fulfilled within campaign timeline
If validation fails, return an error:
{
  "error": {
    "code": "FORMAT_INCOMPATIBLE",
    "message": "Product 'ctv_sports_premium' does not support format 'audio_standard_30s'",
    "field": "packages[0].formats",
    "supported_formats": ["video_standard_30s", "video_standard_15s"]
  }
}

Brand Manifest for Sales Agents

The brand_manifest field is required so publishers know who the advertiser is. This is essential for:
  • Policy compliance: Publishers need to verify advertisers meet their standards
  • Business relationship: Knowing your customer (KYC) requirements
  • Legal accountability: Clear identification of who’s running ads
However, sales agents focused purely on media buying can provide minimal manifests.

When You Need Creative Generation or Optimization

Provide a full brand manifest with guidelines, assets, colors, fonts, and product catalog. See Brand Manifest for complete documentation.

When You’re Only Creating Media Buys

Minimal manifests are perfectly acceptable. You just need to identify who the advertiser is:
{
  "brand_manifest": {
    "name": "ACME Corporation",
    "url": "https://acmecorp.com"
  }
}
Or even simpler for brands with URLs:
{
  "brand_manifest": {
    "url": "https://acmecorp.com"
  }
}
What the minimal manifest provides:
  • Advertiser identity: Publishers know who’s buying ads
  • Policy validation: Publishers can check the brand against their policies
  • Business records: Clear tracking of which brands are running campaigns
What you don’t need to provide:
  • Brand colors, fonts, or logos (only needed for creative generation)
  • Product catalogs (only needed if using promoted_products field or creative generation)
  • Tone or guidelines (only needed for AI-powered creative generation)
The brand manifest is required so publishers always know their customer, but you don’t need to provide extensive brand details unless you’re using creative generation or optimization features.

Usage Notes

  • A media buy represents a complete advertising campaign with one or more packages
  • Each package is based on a single product with specific targeting, budget allocation, and format requirements
  • Format specification is required for each package - this enables placeholder creation and validation
  • Both media buys and packages have buyer_ref fields for the buyer’s reference tracking
  • The brand_manifest field is required and provides brand identity, context, and product catalog (see section above for minimal usage patterns)
  • Publishers will validate the promoted offering against their policies before creating the media buy
  • Package-level targeting overlay applies additional criteria on top of product-level targeting
  • The total budget is distributed across packages based on their individual budget settings (or proportionally if not specified)
  • Budget supports multiple currencies via ISO 4217 currency codes
  • AXE segments (axe_include_segment and axe_exclude_segment) enable advanced audience targeting within the targeting overlay
  • Creative assets must be uploaded before the deadline for the campaign to activate
  • Pending states are normal operational states, not errors
  • Orchestrators MUST NOT treat pending states as errors - they are part of normal workflow

Policy Compliance

The promoted_offering is validated during media buy creation. If a policy violation is detected, the API will return an error:
{
  "error": {
    "code": "POLICY_VIOLATION",
    "message": "Offering category not permitted on this publisher",
    "field": "promoted_offering",
    "suggestion": "Contact publisher for category approval process"
  }
}
Publishers should ensure that:
  • The promoted offering aligns with the selected packages
  • Any uploaded creatives match the declared offering
  • The campaign complies with all applicable advertising policies

Implementation Guide

Generating Helpful Messages

The message field should provide a concise summary that includes:
  • Total budget and key targeting parameters
  • Expected reach or inventory details
  • Clear next steps and deadlines
  • Approval status and expected timelines
def generate_media_buy_message(media_buy, request):
    if media_buy.status == "completed" and media_buy.creative_deadline:
        return f"Successfully created your ${request.total_budget:,} media buy targeting {format_targeting(request.targeting_overlay)}. The campaign will reach {media_buy.estimated_reach:,} users. Please upload creative assets by {format_date(media_buy.creative_deadline)} to activate the campaign."
    elif media_buy.status == "working" and media_buy.requires_approval:
        return f"Your ${request.total_budget:,} media buy has been submitted for approval. {media_buy.approval_reason}. Expected approval time is {media_buy.estimated_approval_time}. You'll receive a notification once approved."
    elif media_buy.status == "completed" and media_buy.is_live:
        return f"Great news! Your ${request.total_budget:,} campaign is now live and delivering to your target audience. Monitor performance using check_media_buy_status."
    elif media_buy.status == "rejected":
        return f"Media buy was rejected: {media_buy.rejection_reason}. Please review the requirements and resubmit."