Scope of Results
Sales agents MUST return every media buy owned by the authenticated account, regardless of how the buy was created — via AdCPcreate_media_buy, via the seller’s own APIs, via manual trafficking, via legacy or third-party systems. Scope is account ownership, not creation surface. A media_buy_id returned here identifies any order in the seller’s ad server accessible to the authenticated caller.
Any media buy returned by get_media_buys MUST be reachable by every task in its valid_actions. Sales agents MUST NOT mark a buy read-only, hide it, or refuse updates on the basis that it was not originally created via AdCP. When an action is unavailable for business reasons (contractual obligations, platform constraints, policy), the seller MUST omit only that action from valid_actions — never the whole set, and never merely because the buy was created outside AdCP. A seller that returns non-AdCP buys with a systematically empty valid_actions is non-conformant; that pattern is indistinguishable from hiding the buy.
Sellers that need to partition inventory away from a caller MUST do so at the account boundary, not within-account. See Account Ownership vs. Creation Surface.
Request Schema: /schemas/3.2.0-beta.0/media-buy/get-media-buys-request.json
Response Schema: /schemas/3.2.0-beta.0/media-buy/get-media-buys-response.json
Request Parameters
*
media_buy_ids filters results to specific media buys. If neither is provided, the query is scope-based and uses status_filter + pagination.
When media_buy_ids are provided, no implicit status filtering is applied. Pass status_filter explicitly if you want to filter identified buys by status.
Deterministic filter contract
Every returnedmedia_buys[] row MUST have a media_buy_id in
media_buy_ids when that filter is present and a status in status_filter
when that filter is present. When both are supplied, both predicates apply to
every row, across every page. A seller MUST NOT accept a schema-valid filter and
return unfiltered account results.
A filter can validly leave the result unchanged when every accessible buy
already matches. Conformance is established from the returned rows’ ID and
status membership—not by requiring filtered and unfiltered response payloads to
differ.
Response
Returns an array of media buys with current status, creative approval state, and optionally delivery snapshots:Media Buy Object
3.1 vocabulary note.
get_media_buys returns the lifecycle state on a nested media_buys[].status field (no envelope collision — nested at depth 1). create_media_buy and update_media_buy success responses return the same state on a top-level media_buy_status field (added in 3.1 to avoid colliding with the envelope task-status status). Same enum, two field names in 3.1 — the cascade unifies in 4.0 (#4905). Buyers maintaining cross-call state should treat the two as the same logical value. See Migration › media_buy_status for the full picture.Package Object
Creative Approval Object
Indicators are deliberately attached here rather than to the creative globally. A creative may be fatigued in one package or publisher context while remaining effective elsewhere.
budget_constrained; packages may carry creative_diversity_low, audience_saturation, inventory_shortfall_forecast, pacing_risk, or budget_constrained. Assignment types remain nested under creative_approvals[]. Omitted indicators is unknown; empty is evaluated-clear only for indicator_types_evaluated and declared coverage. See Indicators and Warnings.
For sellers that advertise inline_creative_management without a creative library, creative_approvals is the only standardized approval-state readback surface for the package’s currently assigned inline creatives. It reports creative_id, aggregate approval_status, optional rejection_reason, and approval_scopes for mixed publisher outcomes, but it does not include the full CreativeAsset payload, placement routing, weights, or prior revisions submitted on create_media_buy or update_media_buy. Buyers should retain their own submitted creative bodies when integrating with inline-only sellers.
Creative revisions are represented as approval_status: "rejected" with a specific rejection_reason. There is no package-level input-required status for creative edits; upload corrected library assets via sync_creatives, or corrected inline-only package assets via packages[].creatives on update_media_buy.
History Entry Object
History entries are append-only — sellers MUST NOT modify or delete previously emitted entries. Callers MAY cache entries by revision number.
revision increments only when the seller applies a mutating state change or update. Reads, validation-only calls, and exact idempotency replays do not create history entries or bump revision. Buyers should treat the returned revision as the token for their next update_media_buy call intended to change state.
confirmed_at is not a delivery status timestamp. It records seller commitment and remains stable through later pause/resume, activation, completion, cancellation, and reporting changes.
Snapshot Object
not_delivering means the package is within its scheduled flight but has delivered zero impressions for at least one full staleness cycle. Implementers must not return not_delivering until staleness_seconds have elapsed since package activation — a new package with no impressions in its first minutes is expected, not a problem. Check start_time to confirm the package is within its flight before acting on this status.
For AdCP-authored buys, budget constraints and canonical bidding always use media_buy.currency. snapshot.currency identifies the reporting denomination for snapshot spend, and legacy/external buys may still carry package.currency; neither changes the denomination of buyer-authored 3.2 controls.
Webhook Activity
Wheninclude_webhook_activity: true, each returned media buy MAY carry a webhook_activity array describing recent webhook fires relevant to that buy. It covers both per-buy reporting/health events and account-level indicators.changed or creative.assignment_changed invalidations whose payload names the buy. This is the buyer-side debug surface for the persistent-channel webhook contract.
The record shape, request-field names, scoping, retention floor, three-state presence, and cardinality rules are uniform across AdCP resources that adopt this surface. See Webhook activity log pattern on the snapshot/log contract page for the cross-resource normative section — the rules below restate it for the media-buy call site and add the media-buy-specific capability gate.
Per-buy delivery events (scheduled, final, delayed, adjusted, window_update) and health events (impairment) use the buy’s push_notification_config; their subscriber_id may be absent in a single-subscriber setup. Indicator and assignment invalidations use account notification_configs[]; their subscriber_id is required. Support for the latter is declared by media_buy.relationship_notifications, including whether this activity log is available. All share the same webhook delivery contract and buyer-side debug need.
Status semantics:
success— response received with a 2xx status.http_status_codepopulated.failed— response received with a non-2xx status.http_status_codepopulated;error_messagedescribes the response.timeout— no response within the seller’s configured timeout.http_status_codenull. Operationally: the buyer’s endpoint is reachable but slow / overloaded.connection_error— DNS, TLS, or socket failure before any HTTP response.http_status_codenull. Operationally: the buyer’s endpoint is unreachable or misconfigured.pending— attempt is in flight or queued for retry.completed_atis null; subsequent attempts appear with the sameidempotency_keyand incrementedattempt.
attempt: 1. A 3-attempt retry trail (e.g., two failures then a success) appears as three records sharing idempotency_key.
Scoping (normative):
webhook_activityMUST be scoped to the calling principal. When multiple buyer principals share visibility into the same media buy via account-level access, each principal sees only fires targeting its own endpoint.- Sellers that surface this field MUST retain records for at least 30 days from each record’s
completed_at— uniformly acrosssuccess,failed,timeout, andconnection_erroroutcomes (all of which populatecompleted_at). For records still inpendingstatus, the clock runs fromfired_atuntil the attempt terminates and then transitions to 30 days fromcompleted_at— retry trails do not age out mid-flight. Sellers that cannot honor this floor MUST omit the field entirely rather than return a shorter window; the three-state presence semantics give them a clean opt-out and buyers a single guarantee they can build against. - This surface is a debug aid, not a full audit log. There is no cursor for older fires beyond
webhook_activity_limit— buyers needing full history must persist webhook records on their own side.
For per-buy events, sellers whose declared
propagation_surfaces excludes webhook omit those records. That does not suppress account-level indicator/assignment records when media_buy.relationship_notifications.supports_webhook_activity is true.
Diagnosing an unexpected omission. For a per-buy event, check the buy’s push_notification_config and the seller’s capabilities.media_buy.propagation_surfaces via get_adcp_capabilities. For an indicator or assignment event, check the account’s notification_configs[] and media_buy.relationship_notifications.supports_webhook_activity. If the relevant registration and capability both check out, omission is a seller-side persistence gap and warrants an operator ticket.
Privacy:
- The
urlfield has its query string and fragment stripped, and sellers SHOULD redact path segments resembling shared secrets (high-entropy random material, UUID / token shapes). - Request and response bodies are not surfaced by this field. A future
include_webhook_payloadsextension may add them under stricter authorization controls — out of scope here. error_messageis a server-side classification string only — never request headers, never response bodies, never buyer-endpoint stack traces.
Diagnose a webhook delivery problem
Valid Actions Mapping
Thevalid_actions array tells agents what operations are permitted on a media buy in its current state. Sellers SHOULD include this field. Expected values by status:
Sellers MAY omit actions based on business rules (e.g., omit
cancel when the media buy has contractual obligations that prevent cancellation).
For creative changes, sync_creatives in valid_actions is a legacy creative-change action label, not proof that the sync_creatives task exists. Use the creative path the seller advertises: sync_creatives and creative_assignments for sellers with creative.has_creative_library: true, or packages[].creatives on update_media_buy for inline-only sellers.
Common Scenarios
Check creative approval status
Monitor delivery with snapshots
Campaign readiness check
Snapshot vs. get_media_buy_delivery
Use
get_media_buys to answer “what is the current state of my campaigns?” and get_media_buy_delivery for “how did my campaigns perform over a period?”
Status taxonomy is shared for lifecycle filters across both tasks (pending_creatives, pending_start, active, paused, completed). get_media_buy_delivery may additionally return reporting-only statuses (reporting_delayed, failed) in webhook contexts.
Data Freshness
Snapshotstaleness_seconds varies by platform:
When the platform only has batch reporting, the seller agent should return the most recent cached data with the appropriate
staleness_seconds.
If include_snapshot: true and snapshot is omitted for a package, check snapshot_unavailable_reason:
SNAPSHOT_UNSUPPORTED: the seller does not support package snapshots for this integrationSNAPSHOT_TEMPORARILY_UNAVAILABLE: snapshot pipeline is delayed or degraded; retry laterSNAPSHOT_PERMISSION_DENIED: caller lacks permission to view snapshot metrics for that package
Pagination
Use cursor pagination for broad status queries to avoid large payloads:- Request: set
pagination.max_results(1-100, default 50) and optionalpagination.cursor - Response: read
pagination.has_more; when true, passpagination.cursorinto the next request - ID-targeted queries (
media_buy_ids) can omit pagination unless the ID set is very large
Error Handling
A valid query with no matching media buys succeeds with an empty
media_buys array.
Next Steps
- Upload missing creatives: Use
sync_creativesfor library-backed sellers, orpackages[].creativesonupdate_media_buyfor inline-only sellers - Investigate zero delivery: Check
delivery_status: "not_delivering"andstart_timeto confirm the flight is active, then useupdate_media_buyto adjust pricing or targeting - Detailed reporting: Use
get_media_buy_deliveryfor date-range reporting and daily breakdowns - Optimize campaigns: Use
provide_performance_feedbackto share results with the seller