Skip to main content
Modify an existing media buy using PATCH semantics. Supports campaign-level and package-level updates. Response Time: Instant to days (status: completed, working < 120s, or submitted for manual review) PATCH Semantics: Only specified fields are updated; omitted fields remain unchanged. Request Schema: /schemas/v3/media-buy/update-media-buy-request.json Response Schema: /schemas/v3/media-buy/update-media-buy-response.json

Quick Start

Create a media buy, then pause it:
import { testAgent } from '@adcp/client/testing';
import { CreateMediaBuyResponseSchema, UpdateMediaBuyResponseSchema } from '@adcp/client';

// First, create a media buy to update
const uniqueRef = `test_campaign_${Date.now()}`;

// Use dates in the future
const startDate = new Date();
startDate.setDate(startDate.getDate() + 7); // Start 1 week from now
const endDate = new Date();
endDate.setDate(endDate.getDate() + 37); // End 5 weeks from now

const createResult = await testAgent.createMediaBuy({
  brand: { domain: 'acmecorp.com' },
  packages: [{
    product_id: 'prod_d979b543',
    pricing_option_id: 'cpm_usd_fixed',
    format_ids: [{
      agent_url: 'https://creative.adcontextprotocol.org',
      id: 'display_300x250_image'
    }],
    budget: 800,
    bid_price: 5.00
  }],
  start_time: startDate.toISOString(),
  end_time: endDate.toISOString()
});

if (!createResult.success) {
  throw new Error(`Create failed: ${createResult.error}`);
}

const created = CreateMediaBuyResponseSchema.parse(createResult.data);
if ('errors' in created && created.errors) {
  throw new Error(`Create failed: ${JSON.stringify(created.errors)}`);
}

console.log(`Created media buy ${created.media_buy_id}`);

// Now update it - pause the campaign
const updateResult = await testAgent.updateMediaBuy({
  media_buy_id: created.media_buy_id,
  paused: true
});

if (!updateResult.success) {
  throw new Error(`Update failed: ${updateResult.error}`);
}

const updated = UpdateMediaBuyResponseSchema.parse(updateResult.data);
if ('errors' in updated && updated.errors) {
  throw new Error(`Update failed: ${JSON.stringify(updated.errors)}`);
}

console.log(`Campaign ${updated.media_buy_id} paused`);

Request Parameters

ParameterTypeRequiredDescription
media_buy_idstringYes*Seller’s media buy identifier to update
revisionintegerNoExpected current revision for optimistic concurrency. Seller rejects with CONFLICT on mismatch. Obtain from get_media_buys or the most recent response.
start_timestringNoUpdated campaign start time
end_timestringNoUpdated campaign end time
pausedbooleanNoPause/resume entire media buy (true = paused, false = active)
canceledbooleanNoCancel the entire media buy (irreversible). Must be true when present. Seller may reject with NOT_CANCELLABLE.
cancellation_reasonstringNoReason for cancellation
packagesPackageUpdate[]NoPackage-level updates (see below)
reporting_webhookobjectNoUpdate reporting webhook configuration (see below)
idempotency_keystringNoUnique key for safe retries. If an update with the same key has already been processed, the seller returns the original response. MUST be unique per (seller, request) pair. Min 16 chars.
invoice_recipientBusinessEntityNoOverride who receives the invoice for this buy. The seller MUST validate authorization and include in check_governance when governance agents are configured.
new_packagesPackageRequest[]NoNew packages to add to this media buy. Same shape as create_media_buy packages. Only supported by sellers that advertise add_packages in valid_actions.
push_notification_configobjectNoWebhook for async operation notifications
media_buy_id is required to identify the media buy to update.

Reporting Webhook Object

Configure automated delivery reporting for this media buy:
ParameterTypeRequiredDescription
urlstringYesWebhook endpoint URL
authenticationobjectYesAuth config with schemes and credentials
reporting_frequencystringYeshourly, daily, or monthly
requested_metricsstring[]NoSpecific metrics to include (defaults to all)
tokenstringNoClient token for validation (min 16 chars)
Note: reporting_webhook configures ongoing campaign reporting. push_notification_config is for async operation notifications (e.g., “notify me when this update completes”).

Package Update Object

ParameterTypeDescription
package_idstringSeller’s package identifier to update
pausedbooleanPause/resume specific package (true = paused, false = active)
canceledbooleanCancel this package (irreversible). Must be true when present. Seller may reject with NOT_CANCELLABLE.
cancellation_reasonstringReason for canceling this package
budgetnumberUpdated budget allocation
impressionsnumberUpdated impression goal for this package
start_timestringUpdated flight start date/time in ISO 8601 format. Must fall within the media buy’s date range.
end_timestringUpdated flight end date/time in ISO 8601 format. Must fall within the media buy’s date range.
pacingstringUpdated pacing strategy
bid_pricenumberUpdated bid price (auction products only). This is the exact bid/price to honor unless the selected pricing option has max_bid: true, in which case it is treated as the buyer’s maximum willingness to pay (ceiling).
optimization_goalsOptimizationGoal[]Replace all optimization goals for this package. Uses replacement semantics — omit to leave goals unchanged.
targeting_overlayTargetingOverlayUpdated targeting restrictions
catalogsCatalog[]Replace the catalogs this package promotes. Uses replacement semantics — omit to leave unchanged.
keyword_targets_addKeywordTarget[]Keyword targets to add or upsert by (keyword, match_type) identity. On create, these are set as keyword_targets inside targeting_overlay.
keyword_targets_removeKeywordTarget[]Keyword targets to remove by (keyword, match_type) identity.
negative_keywords_addNegativeKeyword[]Negative keywords to append to this package. On create, these are set as negative_keywords inside targeting_overlay.
negative_keywords_removeNegativeKeyword[]Negative keywords to remove from this package.
creative_assignmentsCreativeAssignment[]Replace assigned creatives with optional weights and placement targeting
creativesCreativeAsset[]Upload and assign new creatives inline (must not exist in library)
package_id is required to identify the package to update.

Response

Success Response

FieldDescription
media_buy_idMedia buy identifier
statusMedia buy status after the update (present when status changes, e.g., cancellation)
revisionRevision number after this update. Use in subsequent requests for optimistic concurrency.
implementation_dateISO 8601 timestamp when changes take effect (null if pending approval)
invoice_recipientUpdated invoice recipient, echoed from request when provided. Confirms the seller accepted the billing override. Bank details are omitted (write-only).
valid_actionsActions the buyer can perform after this update. Saves a round-trip to get_media_buys.
affected_packagesArray of full Package objects showing complete state after update

Error Response

FieldDescription
errorsArray of error objects explaining failure
Note: Responses use discriminated unions - you get either success fields OR errors, never both. Always check for errors before accessing success fields.

Common Scenarios

Update Package Budget

Increase budget for a specific package:
import { testAgent } from '@adcp/client/testing';
import { UpdateMediaBuyResponseSchema } from '@adcp/client';

const result = await testAgent.updateMediaBuy({
  media_buy_id: 'mb_12345',
  packages: [{
    package_id: 'pkg_001',
    budget: 50000  // Increased from 30000
  }]
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = UpdateMediaBuyResponseSchema.parse(result.data);

if ('errors' in validated && validated.errors) {
  throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`);
}

const pkg = validated.affected_packages?.find(p => p.package_id === 'pkg_001');
if (pkg) {
  console.log(`Package budget updated to ${pkg.budget}`);
}

Change Campaign Dates

Extend campaign end date:
import { testAgent } from '@adcp/client/testing';
import { UpdateMediaBuyResponseSchema } from '@adcp/client';

const result = await testAgent.updateMediaBuy({
  media_buy_id: 'mb_12345',
  end_time: '2025-09-30T23:59:59Z'
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = UpdateMediaBuyResponseSchema.parse(result.data);

if ('errors' in validated && validated.errors) {
  throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`);
}

console.log('Campaign end date extended');
console.log(`Effective: ${validated.implementation_date}`);

Update Targeting

Add or modify geographic restrictions:
import { testAgent } from '@adcp/client/testing';
import { UpdateMediaBuyResponseSchema } from '@adcp/client';

const result = await testAgent.updateMediaBuy({
  media_buy_id: 'mb_12345',
  packages: [{
    package_id: 'pkg_001',
    targeting_overlay: {
      geo_countries: ['US', 'CA'],
      geo_regions: ['US-CA', 'US-NY', 'US-TX', 'CA-ON', 'CA-QC']
    }
  }]
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = UpdateMediaBuyResponseSchema.parse(result.data);

if ('errors' in validated && validated.errors) {
  throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`);
}

console.log('Targeting updated successfully');

Replace Creatives

Swap out creative assignments for a package:
import { testAgent } from '@adcp/client/testing';
import { UpdateMediaBuyResponseSchema } from '@adcp/client';

const result = await testAgent.updateMediaBuy({
  media_buy_id: 'mb_12345',
  packages: [{
    package_id: 'pkg_001',
    creative_assignments: [
      { creative_id: 'creative_video_v2' },
      { creative_id: 'creative_display_v2', weight: 60 }
    ]
  }]
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = UpdateMediaBuyResponseSchema.parse(result.data);

if ('errors' in validated && validated.errors) {
  throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`);
}

const pkg = validated.affected_packages?.find(p => p.package_id === 'pkg_001');
const assignmentCount = pkg?.creative_assignments?.length || 0;
console.log(`Assigned ${assignmentCount} creatives`);

Multiple Package Updates

Update multiple packages in one call:
import { testAgent } from '@adcp/client/testing';
import { UpdateMediaBuyResponseSchema } from '@adcp/client';

const result = await testAgent.updateMediaBuy({
  media_buy_id: 'mb_12345',
  packages: [
    {
      package_id: 'pkg_001',
      budget: 50000,
      pacing: 'front_loaded'
    },
    {
      package_id: 'pkg_002',
      budget: 30000,
      paused: true
    }
  ]
});

if (!result.success) {
  throw new Error(`Request failed: ${result.error}`);
}

const validated = UpdateMediaBuyResponseSchema.parse(result.data);

if ('errors' in validated && validated.errors) {
  throw new Error(`Update failed: ${JSON.stringify(validated.errors)}`);
}

console.log(`Updated ${validated.affected_packages?.length || 0} packages`);

Cancel a Media Buy

Cancel an entire media buy:
{
  "media_buy_id": "mb_12345",
  "canceled": true,
  "cancellation_reason": "Campaign strategy changed"
}
Success response:
{
  "media_buy_id": "mb_12345",
  "status": "canceled",
  "revision": 4,
  "implementation_date": "2025-06-15T10:00:00Z",
  "affected_packages": []
}
NOT_CANCELLABLE error response:
{
  "errors": [{
    "code": "NOT_CANCELLABLE",
    "message": "Media buy mb_12345 has contractual obligations preventing cancellation",
    "suggestion": "Contact seller to discuss cancellation options"
  }]
}
INVALID_STATE error response (e.g., trying to update a completed media buy):
{
  "errors": [{
    "code": "INVALID_STATE",
    "message": "Media buy mb_12345 is in terminal state 'completed' and cannot be modified",
    "suggestion": "Check current status via get_media_buys and adjust request"
  }]
}

Cancel a Package

Cancel a single package while the media buy remains active:
{
  "media_buy_id": "mb_12345",
  "packages": [
    {
      "package_id": "pkg_67890",
      "canceled": true,
      "cancellation_reason": "Underperforming — reallocating budget"
    }
  ]
}

What Can Be Updated

Campaign-Level Updates

Can update:
  • Start/end times (subject to seller approval)
  • Campaign status (active/paused/canceled)
  • Reporting webhook configuration (URL, frequency, metrics)
Cannot update:
  • Media buy ID
  • Brand reference
  • Original package product IDs

Package-Level Updates

Can update:
  • Budget allocation
  • Pacing strategy
  • Bid prices (auction products)
  • Optimization goal (event source, event type, target ROAS/CPA)
  • Targeting overlays
  • Creative assignments
  • Package status (active/paused/canceled)
  • Catalog reference (replace the catalog a catalog-driven package promotes)
  • Creative assignments (before the package’s creative_deadline)
Cannot update:
  • Package ID
  • Product ID
  • Pricing option ID
  • Format IDs (creatives must match existing formats)

Error Handling

Common errors and resolutions:
Error CodeDescriptionResolution
MEDIA_BUY_NOT_FOUNDMedia buy doesn’t existVerify media_buy_id
PACKAGE_NOT_FOUNDPackage doesn’t existVerify package_id
UPDATE_NOT_ALLOWEDField cannot be changedSee “What Can Be Updated” above
BUDGET_INSUFFICIENTNew budget below minimumIncrease budget amount
POLICY_VIOLATIONUpdate violates content policyReview policy requirements
INVALID_STATEOperation not allowed in current state (e.g., updating completed/canceled media buy)Check campaign status via get_media_buys
NOT_CANCELLABLEMedia buy or package cannot be canceledCheck seller’s cancellation policy or contact seller
CREATIVE_REJECTEDCreative failed content policy reviewRevise the creative per the seller’s advertising policies
CREATIVE_DEADLINE_EXCEEDEDCreative change submitted past the package’s creative_deadlineCheck package creative_deadline before submitting creative changes
CREATIVE_ID_EXISTSCreative ID already exists in libraryUse a different creative_id, assign existing creatives via creative_assignments, or update via sync_creatives
BUDGET_EXCEEDEDOperation would exceed allocated budgetReduce the amount or increase media buy total budget
CONFLICTRevision mismatch — another update was applied since you last readRe-read via get_media_buys and retry with current revision
VALIDATION_ERRORRequest format or business rule violationCheck error field and message for specifics
Example error response:
{
  "errors": [{
    "code": "UNSUPPORTED_FEATURE",
    "message": "Cannot change product_id for existing package",
    "field": "packages[0].product_id",
    "suggestion": "Create a new package with the desired product instead"
  }]
}

Update Approval

Some updates require seller approval and return pending status:
  • Significant budget increases (threshold varies by seller)
  • Date range changes affecting inventory availability
  • Targeting changes that alter campaign scope
  • Creative changes requiring policy review
When approval is needed, implementation_date will be null and affected_packages contains the proposed state of each package that would be modified:
{
  "media_buy_id": "mb_12345",
  "implementation_date": null,
  "affected_packages": [
    {
      "package_id": "pkg_abc123",
      "budget": 50000,
      "status": "pending_activation"
    }
  ]
}

PATCH Semantics

Only specified fields are updated - omitted fields remain unchanged:
{
  "media_buy_id": "mb_12345",
  "packages": [{
    "package_id": "pkg_001",
    "budget": 50000
  }]
}
Array replacement: When updating arrays (like creative_assignments), provide the complete new array:
{
  "media_buy_id": "mb_12345",
  "packages": [{
    "package_id": "pkg_001",
    "creative_assignments": [
      { "creative_id": "creative_video_v2" },
      { "creative_id": "creative_display_v2", "weight": 60 }
    ]
  }]
}

Asynchronous Operations

Updates may be asynchronous, especially with seller approval.

Response Patterns

Synchronous (completed immediately) — campaign-level update (e.g., paused: true):
{
  "media_buy_id": "mb_12345",
  "implementation_date": "2025-06-15T10:00:00Z",
  "affected_packages": []
}
Synchronous (completed immediately) — package-level update:
{
  "media_buy_id": "mb_12345",
  "implementation_date": "2025-06-15T10:00:00Z",
  "affected_packages": [
    {
      "package_id": "pkg_abc123",
      "budget": 50000,
      "status": "active"
    }
  ]
}
Asynchronous (processing):
{
  "status": "working",
  "message": "Processing update..."
}
Poll for completion or use webhooks/streaming. Manual Approval Required:
{
  "status": "submitted",
  "message": "Update requires seller approval (2-4 hours)"
}
Will take hours to days.

Protocol-Specific Handling

AdCP tasks work across multiple protocols (MCP, A2A, REST). Each protocol handles async operations differently:
  • Status checking: Polling, webhooks, or streaming
  • Updates: Protocol-specific mechanisms
  • Long-running tasks: Different timeout and notification patterns
See Async Operations for protocol-specific async patterns and examples.

Best Practices

1. Use Precise Updates Update only what needs to change - don’t resend unchanged values. 2. Budget Increases Small incremental increases are more likely to be auto-approved than large jumps. 3. Pause Before Major Changes Pause campaigns before making significant targeting or creative changes to avoid delivery issues. 4. Test with Small Changes Test update workflows with minor changes before critical campaign modifications. 5. Monitor Status Always check response status and implementation_date for approval requirements. 6. Validate Package State Check affected_packages in response to confirm changes were applied correctly.

Usage Notes

  • Updates are atomic - either all changes apply or none do
  • Both media buys and packages can be referenced by publisher IDs
  • Pending states (working, submitted) are normal, not errors
  • Orchestrators MUST handle pending states as part of normal workflow
  • implementation_date indicates when changes take effect (null if pending approval)
  • Inline creatives: The creatives array creates NEW creatives only. To update existing creatives, use sync_creatives. To assign existing library creatives, use creative_assignments in the Package Update object.
Campaign Governance — Modification PhaseWhen a buyer’s account has governance agents configured, sellers MUST call check_governance with media_buy_id, planned_delivery, and phase: "modification" before confirming an update. The governance agent validates change magnitude, budget reallocation, and new parameters against the campaign plan.See the seller integration guide for the full execution check workflow and code example.

Next Steps

After updating a media buy:
  1. Verify Changes: Use get_media_buy_delivery to confirm updates
  2. Upload New Creatives: Use sync_creatives if creative assignments changed
  3. Monitor Performance: Track impact of changes on campaign metrics
  4. Optimize Further: Use provide_performance_feedback for ongoing optimization

Learn More