> ## Documentation Index
> Fetch the complete documentation index at: https://docs.interchange.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Media buy lifecycle

> The AdCP media-buy status values a buyer reads on the wire — what each one means, how a source's manual moderation keeps a buy pending, and how an asynchronously accepted buy resolves through a submitted task.

A [media buy](/v2/buyer/campaigns/media-buys) 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.

<Note>
  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](#three-status-surfaces) before treating any one as
  "the" status.
</Note>

## 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:

| Status              | What it means to a buyer                                                                                                               |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `pending_creatives` | Accepted, but no approved creative is assigned yet, so it cannot launch. The buy waits here until a creative is approved and attached. |
| `pending_start`     | Accepted with creatives ready, but the flight has not started. It launches automatically when the start date arrives.                  |
| `active`            | Live and delivering against the flight.                                                                                                |
| `paused`            | Delivery is halted and resumable — by the buyer, or because the buy was created paused.                                                |
| `completed`         | The flight finished or the budget fully delivered. Terminal.                                                                           |
| `rejected`          | The source declined the buy. Terminal — resubmit only after addressing the reason.                                                     |
| `canceled`          | Stopped before completion, by the buyer or the source. Terminal.                                                                       |

<Note>
  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](#asynchronous-acceptance-the-submitted-task).
</Note>

### 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`.

<Tip>
  `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.
</Tip>

## 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:

```json theme={null}
{
  "status": "submitted",
  "task_id": "task_8f3c1a90-4d2e-4b77-9c1a-3e9b5d2f0a11",
  "message": "Awaiting manual review by the source."
}
```

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](/v2/guides/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:

| Task status      | What you get                                                                                                                                                                                             |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `completed`      | The confirmed `media_buy_id` plus its `media_buy_status` — typically `pending_creatives` or `pending_start` (or `rejected` / `canceled` if the source accepted the buy and then declined or stopped it). |
| `input-required` | The source needs something more from you before it can finish. Inspect the response and `error.suggestion`, then resubmit the call with the correction. See [Tasks](/v2/guides/tasks).                   |
| `failed`         | The operation could not complete — including a source that declined the buy outright; the `error` object explains why.                                                                                   |

<Note>
  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.
</Note>

<Warning>
  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`.
</Warning>

### 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](/v2/guides/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](/v2/storefront/media-buy-approvals/overview), and the buy
**keeps delivering** while the operator decides.

| Decision    | What happens                                                                                                                       |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| **Approve** | The cancel is forwarded to the source and the buy terminates (`canceled`).                                                         |
| **Reject**  | The buy stays `active`. The buyer is notified the cancellation was declined, with the operator's reason. The buyer may re-request. |

<Note>
  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.
</Note>

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`](#pendingreason-whose-side-owns-the-wait) of
`awaiting_cancellation_approval`, not as a status change.

<Warning>
  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.
</Warning>

| Buy state                        | Cancellation behavior                                                                         |
| -------------------------------- | --------------------------------------------------------------------------------------------- |
| Not committed (no forwarded leg) | **Retraction**: forwarded directly as a cancel, no approval.                                  |
| Committed, non-guaranteed        | **Direct cancel**: forwarded to the source immediately, no approval.                          |
| Any guaranteed committed leg     | **Cancellation approval**: operator decides; buy keeps delivering until approved or rejected. |

## `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`).

<Warning>
  `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.
</Warning>

| `pendingReason`                   | Who owns the wait | Meaning                                                                                                                                                    |
| --------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `awaiting_storefront_approval`    | Seller            | The storefront operator has your buy in their review queue                                                                                                 |
| `awaiting_cancellation_approval`  | Seller            | The buyer requested cancellation of a committed guaranteed buy; the storefront operator is deciding whether to approve (terminate) or reject (keep active) |
| `awaiting_source_moderation`      | Seller            | The inventory source accepted the buy asynchronously and is still moderating it — no upstream buy exists yet (task-layer wait)                             |
| `no_creatives_attached`           | You               | The buy reached the source with no creatives attached — nothing is in review anywhere, so the wait cannot resolve on its own. Attach creatives to the buy  |
| `source_rejected_creatives`       | You               | Every creative assigned to the buy was rejected by at least one source agent — replace or fix the creatives                                                |
| `creative_processing_at_source`   | Seller            | The upstream buy exists; the source is processing/moderating its creatives before it is launch-eligible                                                    |
| `awaiting_creative_approval`      | Seller / You      | The upstream buy exists, creatives are attached, and a creative review or assignment is genuinely pending                                                  |
| `forward_failed_retrying`         | Platform          | Forwarding to the source failed transiently; the platform is retrying                                                                                      |
| `forward_failed_needs_correction` | Platform          | Forwarding failed structurally; a correction is needed before it can be retried — see `errorCode`                                                          |
| `accepted_awaiting_trafficking`   | Seller            | Accepted end-to-end; the source has not started trafficking it yet                                                                                         |
| `scheduled_not_started`           | Nobody            | Everything is booked; the flight start date has not arrived                                                                                                |

`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](/v2/buyer/campaigns/media-buys#errorcode--structured-failure-codes).

## 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.

| Surface                  | Field                | Value set                                                                                                                    | What it describes                                                                                                            |
| ------------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| AdCP per-buy (this page) | `media_buy_status`   | `pending_creatives`, `pending_start`, `active`, `paused`, `completed`, `rejected`, `canceled`                                | One buy's lifecycle on the AdCP wire.                                                                                        |
| AdCP account rollup      | `operational_status` | `no_media_buys`, `draft`, `pending_creatives`, `pending_start`, `active`, `paused`, `completed`, `attention_required`        | An account-level rollup across all of a buyer's buys. `no_media_buys`, `draft`, and `attention_required` are rollup-only.    |
| Interchange buyer rollup | `status`             | `DRAFT`, `PENDING_APPROVAL`, `INPUT_REQUIRED`, `ACTIVE`, `PAUSED`, `COMPLETED`, `CANCELED`, `FAILED`, `REJECTED`, `ARCHIVED` | The higher-level Interchange campaign and media-buy rollup. See [Media Buy → Status](/v2/buyer/campaigns/media-buys#status). |

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`:

| AdCP `media_buy_status`              | Interchange `status` |
| ------------------------------------ | -------------------- |
| `pending_creatives`, `pending_start` | `PENDING_APPROVAL`   |
| `active`                             | `ACTIVE`             |
| `paused`                             | `PAUSED`             |
| `completed`                          | `COMPLETED`          |
| `rejected`                           | `REJECTED`           |
| `canceled`                           | `CANCELED`           |

For a multi-agent campaign the rollup also surfaces the **most restrictive**
value across agents (see
[Most-restrictive rule](/v2/buyer/campaigns/media-buys#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`.

<Tip>
  For the Interchange rollup and how to poll it, read
  [Get media buy status](/v2/buyer/campaigns/tasks/get-media-buy-status). The
  `adcp_status` field on that response carries the raw AdCP wire status this page
  defines.
</Tip>

## Pending changes: what is live vs what you asked for

When you update a running buy, 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 delivering, and the one you asked for.

**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`:

```json theme={null}
{
  "mediaBuyId": "mb_QCW9XsBApq",
  "status": "ACTIVE",
  "endTime": "2026-07-27T23:59:59.000Z",
  "pendingChange": {
    "status": "PENDING_APPROVAL",
    "pendingAt": "storefront",
    "reason": "Extended to 8/11 with W5+W6 gross budget",
    "differences": {
      "endTime": {
        "live": "2026-07-27T23:59:59.000Z",
        "proposed": "2026-08-11T23:59:59Z"
      },
      "budget": { "live": 33526.33, "proposed": 50105 }
    }
  }
}
```

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.

<Warning>
  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](/v2/buyer/campaigns/tasks/get-media-buy-status).
</Warning>

### Where each surface reports it

| Surface                                                                  | Reports                   | Pending change                                                          |
| ------------------------------------------------------------------------ | ------------------------- | ----------------------------------------------------------------------- |
| [`get_media_buy`](/v2/buyer/campaigns/media-buys)                        | The live buy              | `pendingChange`, including field-level `differences`                    |
| `get_campaign` → `mediaBuyRefs[]`                                        | Live status per buy       | `pendingChange` with status and layer only, to keep the reference small |
| `get_campaign` → `mediaBuys[].packages[]`                                | Live packages and budgets | Not carried, read `get_media_buy`                                       |
| [`get_media_buy_status`](/v2/buyer/campaigns/tasks/get-media-buy-status) | Live delivery state       | Not carried, this surface answers "is it delivering"                    |

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.

## Related

<CardGroup cols={2}>
  <Card title="Media Buy" href="/v2/buyer/campaigns/media-buys" icon="receipt">
    The media-buy object and the Interchange rollup status table
  </Card>

  <Card title="Tasks" href="/v2/guides/tasks" icon="list-check">
    Polling, webhooks, and back-off for the submitted task envelope
  </Card>

  <Card title="Get media buy status" href="/v2/buyer/campaigns/tasks/get-media-buy-status" icon="signal-stream">
    Poll live AdCP status across sales agents
  </Card>

  <Card title="Glossary" href="/v2/concepts/glossary" icon="book-a">
    One-line definitions of every v2 term
  </Card>
</CardGroup>
