Skip to main content
A media buy carries a lifecycle status on the AdCP wire that tells a buyer whether it is delivering, waiting on something, or finished. This page defines each status, explains why a buy a source is still moderating stays pending_creatives, and shows how a buy a source accepts out-of-band resolves through a submitted task.
The AdCP wire status described here is one of three distinct status surfaces a buyer can see for the same buy. They do not replace each other — see Three status surfaces before treating any one as “the” status.

The AdCP media-buy status

When a buyer’s agent calls a storefront’s AdCP agent (create_media_buy, update_media_buy), a confirmed buy carries a per-buy media_buy_status. It is one of seven values:
A buy that never reached its source reads canceled. If create_media_buy is accepted by the storefront but the forward to the underlying source fails (a malformed request, an unreachable source, a credential fault), no media buy exists upstream and none ever will. That buy is canceled, with cancellation.canceled_by set to seller and the cause in the response errors[] — not pending_start, which promises a launch that cannot happen.rejected is reserved for a source that actually considered the buy and declined it. A transport or credential failure is not a seller’s “no”, so it never reads as one: retrying the same request may well succeed.
There is no pending_approval value in the AdCP media-buy status. Insertion-order signing, governance review, and other pre-issuance steps are not modeled as a buy status — they happen at the task layer, before a media_buy_id exists. See Asynchronous acceptance.

Why a buy reads pending_creatives

pending_creatives is the default landing state for a buy that has been accepted but has nothing launch-ready attached. Two distinct situations both surface as pending_creatives:
  • No creative is assigned yet. The buy exists, but the buyer has not synced or assigned an approved creative to it.
  • The source is moderating the buy. Some sources accept a buy and then park it for manual review — checking creatives, signing an insertion order, or applying a content policy — before it is launch-eligible. While that review is outstanding the buy is accepted but not launch-ready, and reads pending_creatives.
pending_creatives is not a failure. It means “accepted, not yet launchable.” Resolve it by assigning an approved creative (and, where a source moderates, waiting for that review to clear), not by resubmitting the buy.

Paused buys still show setup blockers

Creating a buy with paused: true, or pausing it before its creative arrives, does not replace a more useful setup status. The pause is retained as a latent delivery hold:
  1. With no launch-ready creative, the buy reads pending_creatives.
  2. With creatives ready but a future flight, it reads pending_start.
  3. Once the buy is otherwise ready to launch, it reads paused instead of becoming active.
Resuming during either pending state clears the latent hold but does not bypass the blocker. The buy remains pending until its creative and flight are ready. An underlying ad platform may show its native campaign as paused throughout this setup period to prevent spend; the AdCP status reports the actionable blocker rather than copying that provider control.

Asynchronous acceptance: the submitted task

A source does not always confirm a buy before the response is emitted. When a source accepts a buy out-of-band — parking it for manual moderation, queuing it for batch processing, or awaiting an insertion-order signature — the storefront returns a submitted task envelope instead of a confirmed buy:
There is no media_buy_id on this response — the buy is not confirmed yet. The buyer resolves it the same way as any async operation: poll the task (the AdCP tasks_get operation) with the task_id, or await a webhook if a push-notification config was attached to the original call. See Tasks for the concrete polling endpoint, webhook setup, and back-off mechanics. submitted and working mean the task is still processing — keep polling. Otherwise it resolves to one of three terminal task states, and the outcome lands on its completion artifact:
An update_media_buy edit that a source queues for moderation behaves identically: the storefront returns a submitted task envelope, and the edit’s result lands on the task’s completion artifact. The same poll-or-webhook pattern applies.
A submitted envelope is not a confirmation. Do not treat the task_id as a media_buy_id, and do not assume the buy is live until the task completes with a media_buy_status of active.

Receiving the async result without polling

Instead of polling, a buyer can register a push_notification_config on the originating call so the platform delivers the completion event to a webhook when the task resolves:
  • AdCP/MCP surface — buyers calling a storefront’s create_media_buy or update_media_buy tool directly can include a push_notification_config field in the request body. The platform stores it and calls the buyer’s webhook when the task reaches a terminal state, eliminating the need to poll.
  • REST API surface (execute_campaign) — the platform manages the AdCP-layer webhook on the buyer’s behalf. Buyers receive media buy state transitions through Interchange webhook subscriptions: register one via create_webhook_subscription and subscribe to media_buy.approved, media_buy.rejected, and related event types. See Notifications.
The two paths are complementary — a buyer on the REST API does not need to configure push_notification_config directly; that handshake happens internally between the platform and the seller.

Cancellation

A buyer cancels a media buy by requesting deletion (the AdCP delete operation or update_campaign with action: delete). What happens next depends on whether the buy has been committed to a source and, if so, whether the inventory is guaranteed.

Retraction: uncommitted buys

When the buy has not been forwarded to a source (no reserved inventory, no live leg), the cancel is a retraction: the pending request is withdrawn and forwarded directly as a cancel. No operator approval is needed. The buy transitions to canceled. Non-guaranteed committed buys (buys that have been forwarded but are not guaranteed delivery) also cancel directly with no approval, following the same path as a retraction.

Cancellation approval: committed guaranteed buys

When the buy is committed and the inventory is guaranteed (the buy has been forwarded and reserved with the source as guaranteed delivery), the cancel requires seller approval. The storefront operator sees the request as an APPROVE_MEDIA_BUY_CANCELLATION item in their approvals queue, and the buy keeps delivering while the operator decides.
A buy with any guaranteed leg is treated as guaranteed for cancellation purposes. There is no partial (per-leg) cancel: approval or rejection applies to the entire buy.
While a cancellation is awaiting the operator’s decision, the buy keeps its normal AdCP status and continues delivering. The wait is surfaced as a pendingReason of awaiting_cancellation_approval, not as a status change.
If the forward to the source fails when the operator approves, the buy is not marked canceled. The work item moves to BLOCKED with the failure reason, and the operator can retry. The platform never strands a live order by marking it canceled before the source confirms.

pendingReason: whose side owns the wait

Statuses say that a buy is waiting; they do not say on whom. For storefront-routed buys the platform derives a pendingReason annotation from its forwarding records — per source leg, rolled up to the most-blocking wait — and surfaces it on the buyer’s read surfaces (GET /media-buys/:mediaBuyId, the nested media buy in GET /campaigns/:id, and the status poll as pending_reason).
pendingReason is an annotation, never a status. AdCP’s media-buy status is a closed enum and pre-issuance review is deliberately not a status value — these reasons explain a wait without changing any status field.
pendingSince accompanies the reason with when the current wait began. When a wait turns into a failure or rejection, the buyer-safe errorCode (and the source’s sanitized message) carries the why — see the error-code table.

Three status surfaces

The same buy can be described by three different status vocabularies depending on which surface you read. They answer different questions and use different value sets — keep them distinct. The Interchange buyer rollup is a coarser, derived view of the AdCP per-buy status — not an independent one. Each AdCP value maps to one Interchange value, and pending_creatives and pending_start both collapse to PENDING_APPROVAL: For a multi-agent campaign the rollup also surfaces the most restrictive value across agents (see Most-restrictive rule), so a campaign with one pending_creatives buy and one active buy reports PENDING_APPROVAL. Interchange also has states from its own flow that no AdCP wire value maps to — DRAFT (before execution), INPUT_REQUIRED, FAILED, and ARCHIVED.
For the Interchange rollup and how to poll it, read Get media buy status. The adcp_status field on that response carries the raw AdCP wire status this page defines.

Pending changes: what is live vs what you asked for

When you update a running buy — or extend a completed one — the change is recorded immediately but does not take effect until it is accepted upstream. During that window the buy has two states at once: the one that is live, and the one you asked for. Updating a completed buy follows the same path: the live values (including status: "COMPLETED") remain unchanged, a new version enters PENDING_APPROVAL, and once the seller approves it the new budget and dates take effect — the buy status stays completed. This is how a buyer corrects or extends a budget or flight date after a buy has finished. Every read surface reports what is live. status, endTime, budgets and packages always describe the buy as it is currently delivering. A submitted change never overwrites those values, because a number you cannot tell is live is a number you cannot act on. The change surfaces separately, as pendingChange:
Read it like this:
  • pendingChange absent: nothing is queued. Everything you see is live.
  • pendingChange present: a change was accepted by Interchange but has not reached the delivering buy. differences lists only the fields whose proposed value differs from live. An empty differences means the change is redundant against what is already live.
A buy reading status: "ACTIVE" with a pendingChange is delivering the old values. Do not treat a successful update response as confirmation that the new values are live. Poll until pendingChange disappears, and confirm delivery with has_delivery on get media buy status.

Where each surface reports it

A buy awaiting its first approval is a different case: that buy is itself pending, so status is PENDING_APPROVAL and there is no pendingChange, because nothing is live behind it yet. pendingChange only ever describes a change queued behind an already-established buy.

Media Buy

The media-buy object and the Interchange rollup status table

Tasks

Polling, webhooks, and back-off for the submitted task envelope

Get media buy status

Poll live AdCP status across sales agents

Glossary

One-line definitions of every v2 term