PUT /api/v2/buyer/campaigns/:campaignId
For managed campaigns, the mediaBuys[] array lets you update, cancel, or
delete individual media buys and their packages in a single call. Tracked
tracked projections are read-only; their only accepted compatibility
update is a refresh request that reads from the seller.
Request
Parameters
Retrieve package IDs with Get campaign products, optionally filtered by
mediaBuyId. When the buyer names a package by its period or its dates (“the Week 6 package”, “the display package ending 2026-08-11”), use Get media buy packages instead: it is scoped to one media buy and returns each package’s pacingPeriod and flight window, which is what resolves a description to exactly one id. Package IDs are opaque, so do not derive one by parsing the trailing number on another. Do not submit a real update to test whether an ID is valid: the API validates package ownership before dispatch and returns VALIDATION_ERROR for unknown IDs.
Refresh a tracked campaign
The compatibility refresh request is:Reducing campaign budgets
Lower an executed campaign’s spend through this campaign update endpoint. Do not archive and rebuild a media buy just to reduce its budget. Every budget is gross (fee-inclusive), so the comparison is direct: live media buys can never allocate more thanbudget.total. Lowering
budget.total below what live media buys have already allocated is rejected
with INSUFFICIENT_MEDIA_BUDGET — the error names the new total and the
committed allocation so you know what to shrink or cancel first. Lowering into
headroom (new total at or above the current allocation) succeeds on its own.
To reduce the campaign and its live media buys together, put both changes in
one request: the new budget.total plus explicit
mediaBuys[].packages[].budget reductions. The request is validated against
the projected post-update allocation and applied atomically — if a package
reduction cannot be applied, the campaign-level budget.total is not lowered.
Lower into headroom — budget change alone
Reduce campaign budget and live packages in one call
Propagating geo targeting to live media buys
Changingconstraints.geo_countries or constraints.geo_regions on a campaign
also pushes the new value to every already-deployed package on that campaign’s
media buys — it is not limited to filtering future product discovery. The new
value replaces each package’s existing declaration for that field.
Add a country to a live campaign's targeting
geoConstraintPropagationResults array reports one
entry per media buy with deployed packages:
applied— the seller accepted the new value.unchanged— every package on that media buy already matched the requested value; nothing was sent.rejected— the seller (or its declared capabilities) does not support changing this field on an already-live buy.reasonexplains why. The campaign’s own storedconstraintsstill updates for future product discovery even when a live media buy rejects propagation.
geoConstraintPropagationResults is present whenever the request changes
geo_countries or geo_regions — as an empty array ([]) when the campaign
has no media buy with deployed packages yet (e.g. a DRAFT-only campaign; there
is nothing to propagate to). It is omitted entirely only when the request does
not touch geo_countries or geo_regions at all.
You cannot clear a field to “no value at all” this way: geo_countries: []
(or geo_regions: []) is rejected for any live package that currently has a
non-empty value, since there is no way to declare “no countries” to a seller —
only a non-empty replacement is supported.
A package that you also update explicitly via mediaBuys[].packages[].targetingOverlay
in the same request keeps that explicit value for the field(s) it sets; the
propagated campaign-level value is skipped for exactly those fields on that
package, not the whole package.
This behavior currently covers geo_countries/geo_regions only. Other
constraints fields (language, device_type, device_platform,
geo_metros, geo_postal_areas) are not propagated to live media buys.
Only one update to a given campaign can be applied at a time. A second
request that arrives while an earlier one on the same campaign is still
being applied — even one that doesn’t touch geo_countries/geo_regions —
gets 409 CONFLICT with a message telling you to retry shortly, rather than
risk silently reverting a geo_countries/geo_regions change that already
took effect. This is a brief window, not a queue: a non-geo_countries/
geo_regions request only holds the conflict window for its own write, not
for however long a concurrent geo propagation takes to reach the seller.
Updating a package’s flight dates
Change an individual package’s flight start or end date without canceling and recreating it — useful when one package needs a narrower or extended window than the rest of the media buy.startTime/endTime must fall within the
media buy’s own date range.
Extend one package's flight end date
valid_actions or available_actions for the buy, the request is rejected as
unsupported rather than forwarded speculatively. Wait for seller capabilities
or confirm package date support with the seller first.
Response
pacingPeriods, the response also carries a pacingCascadeResult block at the top level alongside campaign, summarizing the per-media-buy outcome of pushing appended periods to live media buys. See the Pacing periods guide for that shape, append-only rules, and unsupported-agent fallback.
When the request changes constraints.geo_countries or constraints.geo_regions and the campaign has live media buys, the response also carries a geoConstraintPropagationResults array — see Propagating geo targeting to live media buys.
When one or more media buy updates require seller approval (e.g. a seller-managed storefront), the server returns 202 Accepted with a proposals array instead of the campaign object:
Errors
400 VALIDATION_ERROR— acreative_idsentry is not linked to the campaign or does not match a format the media buy’s products accept (the field is not silently filtered); orcreative_idswas supplied withcancel/delete.422 CAPABILITY_NOT_SUPPORTED— a package flight-date update cannot be safely sent because the package has no stored dates and the seller has not declared available actions.409 INSUFFICIENT_MEDIA_BUDGET— the requestedbudget.totalis below the projected live media buy allocation, even after applying the package budget reductions included in the same request. The error names the new total and the committed allocation.409 PRICING_NOT_CONFIGURED— the budget change involves media buys whose fee terms cannot be determined, so the projected allocation cannot be computed.details.unpricedBuyIdsnames the buys; resolve pricing for them first.409 CONFLICT— another update to the same campaign was still being applied when this request arrived. Retry the request.404 NOT_FOUND— campaign or referencedmediaBuyIdnot found.
Related
Get campaign
Read the current resource first
Get media buy status
Poll live ADCP status
Pacing periods
Append-only pacing cascade
Campaign overview
Fields, lifecycle, and concepts