Skip to main content
Modify an existing media buy using PATCH semantics. Supports campaign-level and package-level updates.
update_media_buy is the AdCP 3.x compatibility facade as of 3.2. New integrations use control_media_buy for operational controls and refine_proposals for commercial amendments or negotiated cancellation. Existing payloads remain supported throughout 3.x.
Response Time: Instant to days (status: completed, working < 120s, or submitted for manual review)

Scope

update_media_buy operates on any media_buy_id returned by get_media_buys, not just buys created via create_media_buy. Sales agents MUST NOT refuse updates on the basis that a buy was originally created outside AdCP (direct ad-server entry, legacy APIs, manual trafficking). Creation surface is not a supported axis of authorization; account ownership is. When a specific action is unsupported for a given buy for business reasons (contractual obligations, platform constraints, policy), the seller MUST omit only that action from valid_actions (and the corresponding entry from available_actions[]) on the buy rather than silently rejecting the corresponding update. Creation surface is not a business reason. Sellers MUST NOT return INVALID_STATE on an otherwise-valid update against a non-AdCP-created buy, and MUST NOT return a buy with systematically empty valid_actions simply because it was booked outside AdCP — that pattern is indistinguishable from hiding the buy and violates the Account Ownership vs. Creation Surface rule.

Action vocabulary and field mapping

Buyers express intent through actions; the seller declares the actions available on each buy via the structured available_actions[] field (authoritative) and the flat valid_actions[] field (legacy, deprecated in 4.0). When both fields are present, consumers MUST prefer available_actions[] — it carries the resolved mode, optional sla, and optional terms_ref that the flat string array cannot represent. When a buyer issues an update_media_buy request, the seller maps the request’s fields to one or more actions and rejects with ACTION_NOT_ALLOWED (carrying attempted_action, reason, and currently_available_actions in error.details) if any mapped action is not in the buy’s resolved available_actions[]. The mapping is normative — sellers and SDKs MUST use this table to translate between request fields and action identifiers so the surface is consistent across implementations. The direction-of-change actions (extend_flight / shorten_flight, increase_budget / decrease_budget / reallocate_budget) share their update_fields paths; the action is determined by comparing the requested value against the buy’s current state, not by which field is set. Server-side dispatch enforcement MUST diff request-vs-current to pick the right action and reject with ACTION_NOT_ALLOWED if the resolved action is not in the buy’s available_actions[]. For seller-optimized constraints, clearing packages[].budget or either scope’s daily_budget_cap with null resolves to increase_budget because it removes a hard ceiling. Clearing packages[].min_spend_target resolves to update_budget_allocation. A request clearing both requires both actions when the seller enforces field-level actions conjunctively. Sellers that do not advertise the required action reject the update without mutation; seller_optimized_budget: true alone does not promise mid-flight constraint changes. The coarse legacy actions (update_budget, update_dates, update_packages, sync_creatives) cover the finer-grained vocabulary in a single value for sellers still emitting the 3.0 enum surface. Sellers SHOULD migrate to the finer set; the legacy values are removed in 4.0.

Action modes

Each entry in available_actions[] carries a singular mode (resolved against the buy’s current state). On the product-level allowed_actions[] template the field is modes[] (plural) because a product can offer multiple conditional modes (e.g. self_serve within tolerances, escalating to requires_approval outside). Buyer SDKs MUST branch on mode to decide whether to expect a synchronous response, conditional handling, or an asynchronous approval callback. Requotes are not modeled as an action mode in 3.1; sellers return REQUOTE_REQUIRED when a requested update exceeds the current quoted envelope. PATCH Semantics: Only specified fields are updated; omitted fields remain unchanged. Request Schema: /schemas/3.2.0-beta.0/media-buy/update-media-buy-request.json Response Schema: /schemas/3.2.0-beta.0/media-buy/update-media-buy-response.json

Quick Start

Create a media buy, then pause it:

Request Parameters

account and media_buy_id are always required.

Atomic total-budget updates

total_budget supports two code paths depending on whether explicit package mutations are also present in the same request. Redistribution path (packages and new_packages absent): the seller invokes proportional redistribution as described below. Assertion path (packages or new_packages present): the seller applies the explicit package mutations and requires total_budget.amount to equal the resulting fixed-mode package sum. If the total is inconsistent, the seller MUST return VALIDATION_ERROR and apply no changes. For the redistribution path on a fixed-allocation buy, sellers MUST derive each active package’s new budget from its share of the current active-package budget total. All derived budgets are accepted or rejected as one mutation. If the active total is zero, a package lacks a usable committed budget, or any package cannot accept its derived amount, the seller rejects the whole update. Sellers MUST preserve the requested aggregate exactly, assigning any representation-level rounding remainder deterministically to the final active package. Canceled packages are not redistributed and remain unchanged. For a seller-optimized buy, total_budget updates the shared hard pool. Package budget values remain optional caps rather than allocations and are not rescaled. In either mode, a supplied stale revision, currency mismatch, or exact idempotency replay follows the normal whole-request rules. On fixed-mode success, affected_packages MUST contain the full post-update state of every rescaled active package so the buyer can audit the resulting allocation without a second read.

Optimistic concurrency

revision is the expected current media-buy revision. Some implementations call this value expected_revision internally; on the AdCP wire the field is revision. The field is optional for backward compatibility. When it is present, sellers MUST check it atomically with the write that applies a mutating update; reading the current value in application code, comparing it, then writing later can race another writer and lose an update. If the stored revision differs from the request’s revision, the seller rejects with CONFLICT and applies no changes. Every mutating update increments revision and returns the new value in the success response. Validation-only requests, reads, and exact idempotency replays do not increment it; exact replays return the prior revision. Clients SHOULD pass the latest observed revision on every update intended to change state, then reload with get_media_buys and retry with a fresh idempotency_key when they receive CONFLICT.

Reporting Webhook Object

Configure automated delivery reporting for this media buy: 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

package_id is required to identify the package to update. Sellers validate targeting and creative-assignment replacements atomically. They must never silently delete an assignment or retain an assignment reference to a placement removed from the package’s purchased set. For budget/pacing interaction and bidding inheritance, see Budget & Pacing Controls and BiddingPolicy Object on the create_media_buy reference.

Response

Success Response

Error Response

Note: Responses use discriminated unions. Terminal errors never carry warnings; warnings occur only on successful updates. See Indicators and Warnings.

Common Scenarios

Update Package Budget

Increase budget for a specific package:

Change Campaign Dates

Extend campaign end date:

Update Targeting

Add or modify geographic restrictions:

Replace Creatives

Swap out creative assignments for a package:

Multiple Package Updates

Update multiple packages in one call:

Cancel a Media Buy

Cancel an entire media buy:
Success response:
The body-level media_buy_status is the canonical field for the buy’s lifecycle state. The AdCP 3.2 source schema no longer permits the legacy top-level status: MediaBuyStatus form (e.g., "status": "canceled") because it collides with the envelope task-status at the same root key. That legacy lifecycle field existed only during the 3.1 deprecation window; in 3.2, root status is TaskStatus only. See Migration › media_buy_status for migration guidance. NOT_CANCELLABLE error response:
INVALID_STATE error response (e.g., trying to update a completed media buy):
REQUOTE_REQUIRED error response (update changes the parameter envelope the quote was priced against):

Cancel a Package

Cancel a single package while the media buy remains active:

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 (schema-enforced via not constraint on package-update.json):
  • Package ID
  • Product ID
  • Pricing option ID
  • Format selectors: format_option_refs, format_kind, and params (plus deprecated format_ids on a legacy package); creatives must match the existing package contract
⚠️ Append-only on update:
  • committed_metrics — sellers accept new entries (mid-flight metric additions, each with its own committed_at timestamp) but MUST reject attempts to modify or remove existing entries with validation_error (code IMMUTABLE_FIELD). Runtime enforcement; the append-only semantics aren’t expressible in the schema’s not clause.

Error Handling

Common errors and resolutions: Example error response:

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 full post-update state of each package that would be modified:

PATCH Semantics

Only specified fields are updated - omitted fields remain unchanged:
Array replacement: When updating arrays (like creative_assignments), provide the complete new array:

Asynchronous Operations

Updates may be asynchronous, especially with seller approval.

Response Patterns

Synchronous (completed immediately) — campaign-level update (e.g., paused: true):
Synchronous (completed immediately) — package-level update:
Asynchronous (processing):
Poll for completion or use webhooks/streaming. Manual Approval Required:
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 replaces the package’s inline creative bodies. If the seller advertises creative.has_creative_library: true, use sync_creatives to update existing library creatives and creative_assignments to assign existing library creatives. If the seller advertises inline_creative_management: true without a creative library, use packages[].creatives here for inline creative add, replace, and removal workflows.
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. Supply New Creatives: Use sync_creatives for library-backed sellers, or packages[].creatives in this task for inline-only sellers
  3. Monitor Performance: Track impact of changes on campaign metrics
  4. Optimize Further: Use provide_performance_feedback for ongoing optimization

Learn More