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

> Review the queue of pending media buys and approve or reject them before they reach inventory

When a buyer submits a media buy to your storefront, it lands in your approval queue as a `pending` entry holding the raw AdCP `create_media_buy` payload. You inspect it and record a decision: `approved` or `rejected`. Approved entries are forwarded upstream to the underlying sales agent by the storefront's MCP layer; the `forwardedAt` stamp tells you when that succeeded.

For when this queue is used — the `mediaBuyApproval` setting (and why `manual` still auto-forwards clearly on-policy buys), the lowest-risk "review everything" posture, and how you find out something is waiting — see [Reviewing buyer transactions](/v2/storefront/approvals/overview).

## What the pre-screen verdict means

Each queued entry carries the deterministic pre-screen verdict: *clearly on policy* (would have auto-forwarded), *needs a look*, or *clearly against an explicit rule you wrote*. That last bucket is **decision-support only** — the platform does **not** auto-reject; a clearly-against-policy buy still queues for your decision. For the full verdict model, the one-way AI second opinion, and how the agent reads your written policy, see [Acceptance policy](/v2/concepts/acceptance-policy).

<Note>
  A buyer you've opted into [per-buyer auto-approve](/v2/storefront/buyer-auto-approvals/overview) never appears in this queue — their media buys forward without a pending entry. If you expect a buyer's buys and never see them here, check your auto-approve overrides.
</Note>

An entry moves through `pending → approved` or `pending → rejected`. Decisions are terminal — deciding an entry that is no longer `pending` is rejected, so a double-decide cannot slip through. The `revoked` state covers buyer- or system-initiated cancellation after a decision and is not something you set on the decide endpoint. Each entry is keyed by the buyer's AdCP `mediaBuyId`.

The portable Approvals Page keeps the decision and its forwarding result
separate. If your approval is recorded but delivery to one or more sources is
incomplete, it shows a partial-success notice and lets you explicitly confirm a
typed retry without leaving the Page. The same idempotency key is reused, so a
source that already received the buy cannot create a duplicate booking. A
source response that means "accepted and still processing" does not offer a
duplicate retry.

A forward a source refuses structurally is usually not a dead end. The entry is
stamped `forwardedAt` and the platform stops re-attempting it — re-sending an
identical payload to a source that refused it would fail the same way every
time. Once the underlying cause is fixed, the API can recover the buy in place:
[Retry forwarding](/v2/storefront/media-buy-approvals/tasks/retry-forward) with
`forceTerminal` re-sends it under the same `mediaBuyId` with your approval
intact, so the buyer does not need to resubmit under a new id. This is an API
control; the chat and Pending operations surfaces still offer escalation rather
than a retry for these buys. A buy terminalized *before* any source was
contacted — a paused storefront, an unconfigured settlement currency, an expired
FX quote — cannot be recovered this way and must be resubmitted.

## Buyer task callbacks

Buyer agents can attach AdCP push-notification config to the original `create_media_buy` call. The storefront stores that callback configuration separately from the operator-facing payload and uses it when the approval resolves.

```json theme={null}
{
  "media_buy_id": "mb_2026_q2_ctv",
  "packages": [
    {
      "product_id": "run-of-site",
      "budget": 50000
    }
  ],
  "push_notification_config": {
    "url": "https://buyer.example.com/adcp/tasks",
    "token": "buyer-echo-token",
    "authentication": {
      "schemes": ["HMAC-SHA256"],
      "credentials": "shared-webhook-secret"
    }
  }
}
```

Use `push_notification_config` for canonical AdCP requests. The storefront also accepts `pushNotificationConfig` from clients that pass camelCase JSON through a REST/MCP proxy. Callback URLs must be public HTTPS endpoints; localhost, private-network, link-local, metadata, and internal hostnames are rejected. Supported authentication schemes are `Bearer` and `HMAC-SHA256`.

When the operator approves and every underlying source accepts the forwarded buy, the buyer webhook receives a signed ADCP task event with `status: "completed"` and a `result` containing the accepted `media_buy_id`. When the operator rejects, or approval succeeds but source forwarding fails, the webhook receives `status: "failed"` with an AdCP-compatible error object. Polling the submitted task remains the fallback if no callback is supplied or delivery fails.

Media-buy task events use the AdCP task protocol `media-buy`; creative approval task events use `creative`. These webhook protocol values are distinct from the snake-case `media_buy` value advertised by storefront capabilities.

<Note>
  The approval row returned to storefront operators omits `push_notification_config` and `pushNotificationConfig` from `submittedPayload` so webhook credentials are not exposed in the queue UI or REST responses.
</Note>

All examples use the storefront base URL:

```
https://api.interchange.io/api/v2/storefront
```

Authenticate every request with `Authorization: Bearer $SCOPE3_API_KEY`.

## Task reference

<CardGroup cols={2}>
  <Card title="List approvals" href="/v2/storefront/media-buy-approvals/tasks/list-approvals" icon="list">
    `GET /media-buy-approvals` — the approval queue, newest first
  </Card>

  <Card title="Get an approval" href="/v2/storefront/media-buy-approvals/tasks/get-approval" icon="magnifying-glass">
    `GET /media-buy-approvals/{mediaBuyId}` — one queue entry
  </Card>

  <Card title="Decide an approval" href="/v2/storefront/media-buy-approvals/tasks/decide-approval" icon="gavel">
    `POST /media-buy-approvals/{mediaBuyId}/decide` — approve or reject
  </Card>

  <Card title="Retry forwarding" href="/v2/storefront/media-buy-approvals/tasks/retry-forward" icon="rotate-right">
    `POST /media-buy-approvals/{mediaBuyId}/retry-forward` — re-send an approved buy
  </Card>
</CardGroup>
